From af84212eb467010406ec17051b0f0a4595009246 Mon Sep 17 00:00:00 2001 From: Terry Mancey Date: Mon, 28 Oct 2024 11:33:37 -0500 Subject: [PATCH] [ads] SmartNTT code health --- browser/brave_ads/ads_service_delegate.cc | 19 +- browser/brave_ads/ads_service_delegate.h | 2 + components/brave_ads/core/internal/BUILD.gn | 16 +- .../catalog_new_tab_page_ad_wallpaper_info.h | 4 +- .../creative_new_tab_page_ad_wallpaper_info.h | 4 +- ...w_tab_page_ad_wallpapers_database_table.cc | 2 +- ...reative_new_tab_page_ads_database_table.cc | 7 +- ..._page_ad_serving_condition_matcher_util.cc | 58 -- ...serving_condition_matcher_util_internal.cc | 335 -------- ..._serving_condition_matcher_util_internal.h | 74 -- ...ondition_matcher_util_internal_unittest.cc | 761 ------------------ .../core/internal/serving/targeting/README.md | 2 +- .../targeting/condition_matcher/README.md | 6 + .../condition_matcher_util.cc | 72 ++ .../condition_matcher_util_unittest.cc} | 60 +- .../condition_matcher/matchers/README.md | 5 + .../epoch_operator_condition_matcher_util.cc | 75 ++ .../epoch_operator_condition_matcher_util.h | 20 + ...perator_condition_matcher_util_unittest.cc | 153 ++++ ...perator_condition_matcher_util_internal.cc | 84 ++ ...operator_condition_matcher_util_internal.h | 38 + ...ondition_matcher_util_internal_unittest.cc | 135 ++++ .../pattern_condition_matcher_util.cc | 17 + .../matchers/pattern_condition_matcher_util.h | 18 + ...pattern_condition_matcher_util_unittest.cc | 36 + .../matchers/regex_condition_matcher_util.cc | 22 + .../matchers/regex_condition_matcher_util.h | 18 + .../regex_condition_matcher_util_unittest.cc | 36 + .../condition_matcher/prefs/README.md | 5 + .../prefs/condition_matcher_pref_util.cc | 28 + .../prefs/condition_matcher_pref_util.h | 28 + .../condition_matcher_pref_util_unittest.cc | 44 + .../condition_matcher_pref_util_internal.cc | 178 ++++ .../condition_matcher_pref_util_internal.h | 45 ++ ...ion_matcher_pref_util_internal_unittest.cc | 412 ++++++++++ components/brave_ads/core/public/BUILD.gn | 2 +- .../condition_matcher_util.h} | 37 +- components/brave_ads/core/test/BUILD.gn | 9 +- .../browser/ntp_sponsored_images_data.h | 4 +- .../browser/view_counter_service.cc | 8 +- .../browser/view_counter_service.h | 6 +- 41 files changed, 1569 insertions(+), 1316 deletions(-) delete mode 100644 components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util.cc delete mode 100644 components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.cc delete mode 100644 components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.h delete mode 100644 components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal_unittest.cc create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/README.md create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/condition_matcher_util.cc rename components/brave_ads/core/internal/serving/{new_tab_page_ad_serving_condition_matcher_util_unittest.cc => targeting/condition_matcher/condition_matcher_util_unittest.cc} (62%) create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/README.md create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.cc create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util.h create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/epoch_operator_condition_matcher_util_unittest.cc create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.cc create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.h create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal_unittest.cc create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.cc create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util.h create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/pattern_condition_matcher_util_unittest.cc create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.cc create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util.h create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/regex_condition_matcher_util_unittest.cc create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/README.md create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.cc create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util.h create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/condition_matcher_pref_util_unittest.cc create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.cc create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.h create mode 100644 components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal_unittest.cc rename components/brave_ads/core/public/serving/{new_tab_page_ad_serving_condition_matcher_util.h => targeting/condition_matcher/condition_matcher_util.h} (76%) diff --git a/browser/brave_ads/ads_service_delegate.cc b/browser/brave_ads/ads_service_delegate.cc index 61115155b0ea..4210b284d248 100644 --- a/browser/brave_ads/ads_service_delegate.cc +++ b/browser/brave_ads/ads_service_delegate.cc @@ -54,6 +54,16 @@ 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); +} + void AdsServiceDelegate::OpenNewTabWithUrl(const GURL& url) { #if BUILDFLAG(IS_ANDROID) // ServiceTabLauncher can currently only launch new tabs @@ -146,20 +156,13 @@ 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]:operating_system.name", version_info::GetOSType()) .Set("[virtual]:build_channel.name", version_info::GetChannelString(chrome::GetChannel())) .Set("[virtual]:browser_version", version_info::GetVersionNumber()) .Set("[virtual]:default_search_engine.name", - base::UTF16ToUTF8(template_url_data->short_name())); + GetDefaultSearchEngineName()); } } // namespace brave_ads diff --git a/browser/brave_ads/ads_service_delegate.h b/browser/brave_ads/ads_service_delegate.h index 7a18de6f1876..e34dcdb51f50 100644 --- a/browser/brave_ads/ads_service_delegate.h +++ b/browser/brave_ads/ads_service_delegate.h @@ -47,6 +47,8 @@ class AdsServiceDelegate : public AdsService::Delegate { ~AdsServiceDelegate() override; + std::string GetDefaultSearchEngineName(); + // 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 1f5a418cf60e..8757513d0654 100644 --- a/components/brave_ads/core/internal/BUILD.gn +++ b/components/brave_ads/core/internal/BUILD.gn @@ -835,9 +835,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", @@ -942,6 +939,19 @@ 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/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/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 48f469f99500..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; - NewTabPageAdConditionMatcherMap condition_matchers; + ConditionMatcherMap condition_matchers; }; using CatalogNewTabPageAdWallpaperList = 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 abbce35cfa8d..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; - NewTabPageAdConditionMatcherMap 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 425383c184d6..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 NewTabPageAdConditionMatcherMap& 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 932ec8985055..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; -NewTabPageAdConditionMatcherMap 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); - NewTabPageAdConditionMatcherMap 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/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 b844d4298131..000000000000 --- a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util.cc +++ /dev/null @@ -1,58 +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 { - -namespace { - -constexpr char kNotOperatorPrefix[] = "[!]:"; - -std::string NormalizePrefPath(const std::string& pref_path) { - return pref_path.starts_with(kNotOperatorPrefix) - ? pref_path.substr(/*pos=*/std::strlen(kNotOperatorPrefix)) - : pref_path; -} - -bool MatchCondition(const std::string_view value, - const std::string_view condition) { - return MatchOperator(value, condition) || MatchPattern(value, condition) || - MatchRegex(value, condition); -} - -} // namespace - -bool MatchConditions( - const PrefProviderInterface* const pref_provider, - const NewTabPageAdConditionMatcherMap& 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`, it means that the condition should - // match if the pref path does not exist. - const bool has_not_operator = pref_path.starts_with(kNotOperatorPrefix); - - const std::string normalized_pref_path = NormalizePrefPath(pref_path); - if (const std::optional value = MaybeGetPrefValueAsString( - pref_provider, normalized_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_internal.cc b/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.cc deleted file mode 100644 index bca8cb7759cd..000000000000 --- a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.cc +++ /dev/null @@ -1,335 +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 -#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 { - -constexpr char kOperatorConditionMatcherPrefixPattern[] = "[?]:*"; -constexpr char kEqualOperatorConditionMatcherPrefix[] = "[=]:"; -constexpr char kGreaterThanOperatorConditionMatcherPrefix[] = "[>]:"; -constexpr char kGreaterThanOrEqualOperatorConditionMatcherPrefix[] = "[≥]:"; -constexpr char kLessThanOperatorConditionMatcherPrefix[] = "[<]:"; -constexpr char kLessThanOrEqualOperatorConditionMatcherPrefix[] = "[≤]:"; - -constexpr int32_t kMaxUnixEpochTimestamp = std::numeric_limits::max(); - -std::optional MaybeGetRootPrefValue( - const PrefProviderInterface* const pref_provider, - const std::string& pref_path) { - CHECK(pref_provider); - - if (pref_path.starts_with(kVirtualPrefPathPrefix)) { - return pref_provider->GetVirtualPref(pref_path); - } - - if (std::optional pref_value = - pref_provider->GetProfilePref(pref_path)) { - return pref_value; - } - - if (std::optional pref_value = - pref_provider->GetLocalStatePref(pref_path)) { - return pref_value; - } - - // Unknown pref path. - return std::nullopt; -} - -std::optional MaybeGetDictPrefValue(const base::Value& pref_value, - const std::string& key) { - if (const base::Value* const value = pref_value.GetDict().Find(key)) { - return value->Clone(); - } - - // Unknown pref path key. - return std::nullopt; -} - -std::optional MaybeGetListPrefValue(const base::Value& pref_value, - const std::string& key) { - int index; - if (!base::StringToInt(key, &index)) { - // Invalid pref path key, because this should be an integer index for the - // list. - return std::nullopt; - } - - const base::Value::List& list = pref_value.GetList(); - - if (index < 0 || index >= static_cast(list.size())) { - // Invalid pref path key, because the list index is out of bounds. - return std::nullopt; - } - - return list[index].Clone(); -} - -std::optional MaybeGetNextPrefValue(const base::Value& pref_value, - const std::string& key) { - if (pref_value.is_dict()) { - return MaybeGetDictPrefValue(pref_value, key); - } - - if (pref_value.is_list()) { - return MaybeGetListPrefValue(pref_value, key); - } - - 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, kOperatorConditionMatcherPrefixPattern)); - - 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)); -} - -std::optional ParseTimeDelta(const std::string_view value) { - double timestamp; - if (!base::StringToDouble(value, ×tamp)) { - return std::nullopt; - } - - return TimeDeltaSinceEpoch(static_cast(timestamp)); -} - -bool MatchOperator(const std::string_view value, - const std::string_view condition) { - if (!base::MatchPattern(condition, kOperatorConditionMatcherPrefixPattern)) { - // 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 SmartNTT " << value << " timestamp operator for " - << condition << " 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 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(const std::string_view value, - const std::string_view condition) { - return base::MatchPattern(value, condition); -} - -std::optional MaybeGetPrefValue( - const PrefProviderInterface* const pref_provider, - const std::string& pref_path) { - CHECK(pref_provider); - - // Split the `pref_path` into individual keys using '|' as the delimiter. - const std::vector keys = base::SplitString( - pref_path, "|", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); - if (keys.empty()) { - // Invalid pref path. - VLOG(1) << "Invalid SmartNTT pref path: " << pref_path; - return std::nullopt; - } - - std::optional pref_value; - - for (auto iter = keys.cbegin(); iter != keys.cend(); ++iter) { - const std::string& key = *iter; - - if (!pref_value) { - // Attempt to get the root pref value using the current key. - if (std::optional root_pref_value = - MaybeGetRootPrefValue(pref_provider, key)) { - pref_value = std::move(*root_pref_value); - continue; - } - - // Unknown pref path key. - VLOG(1) << "Unknown SmartNTT " << key << " key for " << pref_path - << " pref path"; - return std::nullopt; - } - - // Attempt to get the next pref value in the path. - pref_value = MaybeGetNextPrefValue(*pref_value, key); - if (!pref_value) { - // Unknown pref path key. - VLOG(1) << "Unknown SmartNTT " << key << " key for " << pref_path - << " pref path"; - return std::nullopt; - } - - if (pref_value->is_dict() || pref_value->is_list()) { - // Continue iterating if the current pref value is a dictionary or list. - continue; - } - - 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 - << " pref path"; - return std::nullopt; - } - - break; - } - - // Return the last pref path value. - 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/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 99b97e360ff0..000000000000 --- a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal.h +++ /dev/null @@ -1,74 +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); - -// Parses a time delta from a string representation. Returns `std::nullopt` if -// the value is invalid. -std::optional ParseTimeDelta(std::string_view value); - -// 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 1a71a26b7378..000000000000 --- a/components/brave_ads/core/internal/serving/new_tab_page_ad_serving_condition_matcher_util_internal_unittest.cc +++ /dev/null @@ -1,761 +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, - 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(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - 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(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - 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(BraveAdsNewTabPageAdServingConditionMatcherUtilInternalTest, - DoNotParseTimeDelta) { - // Act & Assert - EXPECT_FALSE(ParseTimeDelta("broken time")); -} - -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..cd063a4aeae0 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/condition_matcher_util.cc @@ -0,0 +1,72 @@ +/* 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/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) || + 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 62% 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 9df14cf18771..48669af20477 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 NewTabPageAdConditionMatcherMap condition_matchers = { + const ConditionMatcherMap condition_matchers = { {prefs::kSubdivisionTargetingUserSelectedSubdivision, "AUTO"}, {prefs::kOptedInToNotificationAds, "1"}}; @@ -46,11 +44,9 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_TRUE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - MatchEqualOperatorCondition) { +TEST_F(BraveAdsConditionMatcherUtilTest, MatchEqualOperatorCondition) { // Arrange - const NewTabPageAdConditionMatcherMap condition_matchers = { - {prefs::kServeAdAt, "[=]:7"}}; + const ConditionMatcherMap condition_matchers = {{prefs::kServeAdAt, "[=]:7"}}; AdvanceClockBy(base::Days(7)); @@ -58,11 +54,9 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_TRUE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - DoNotMatchEqualOperatorCondition) { +TEST_F(BraveAdsConditionMatcherUtilTest, DoNotMatchEqualOperatorCondition) { // Arrange - const NewTabPageAdConditionMatcherMap condition_matchers = { - {prefs::kServeAdAt, "[=]:7"}}; + const ConditionMatcherMap condition_matchers = {{prefs::kServeAdAt, "[=]:7"}}; AdvanceClockBy(base::Days(7) - base::Milliseconds(1)); @@ -70,10 +64,9 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_FALSE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - MatchPatternCondition) { +TEST_F(BraveAdsConditionMatcherUtilTest, MatchPatternCondition) { // Arrange - const NewTabPageAdConditionMatcherMap condition_matchers = { + const ConditionMatcherMap condition_matchers = { {prefs::kSubdivisionTargetingUserSelectedSubdivision, "?UT*"}}; // Value is "AUTO" in the pref. @@ -81,10 +74,9 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_TRUE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - DoNotMatchPatternCondition) { +TEST_F(BraveAdsConditionMatcherUtilTest, DoNotMatchPatternCondition) { // Arrange - const NewTabPageAdConditionMatcherMap condition_matchers = { + const ConditionMatcherMap condition_matchers = { {prefs::kSubdivisionTargetingUserSelectedSubdivision, "?FOO*"}}; // Value is "AUTO" in the pref. @@ -92,10 +84,9 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_FALSE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - MatchRegexCondition) { +TEST_F(BraveAdsConditionMatcherUtilTest, MatchRegexCondition) { // Arrange - const NewTabPageAdConditionMatcherMap condition_matchers = { + const ConditionMatcherMap condition_matchers = { {prefs::kSubdivisionTargetingUserSelectedSubdivision, "^AU"}}; // Value is "AUTO" in the pref. @@ -103,10 +94,9 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_TRUE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, - DoNotMatchRegexCondition) { +TEST_F(BraveAdsConditionMatcherUtilTest, DoNotMatchRegexCondition) { // Arrange - const NewTabPageAdConditionMatcherMap condition_matchers = { + const ConditionMatcherMap condition_matchers = { {prefs::kSubdivisionTargetingUserSelectedSubdivision, "^FOO"}}; // Value is "AUTO" in the pref. @@ -114,30 +104,28 @@ TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, EXPECT_FALSE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, +TEST_F(BraveAdsConditionMatcherUtilTest, DoNotMatchConditionsIfPrefPathWasNotFound) { // Arrange - const NewTabPageAdConditionMatcherMap 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 NewTabPageAdConditionMatcherMap condition_matchers = { - {"[!]:foo.bar", "baz"}}; + const ConditionMatcherMap condition_matchers = {{"[!]:foo.bar", "baz"}}; // Act & Assert EXPECT_TRUE(MatchConditions(&pref_provider_, condition_matchers)); } -TEST_F(BraveAdsNewTabPageAdServingConditionMatcherUtilTest, +TEST_F(BraveAdsConditionMatcherUtilTest, DoNotMatchConditionsIfAllConditionsAreFalse) { // Arrange - const NewTabPageAdConditionMatcherMap condition_matchers = { + const ConditionMatcherMap condition_matchers = { {prefs::kOptedInToNotificationAds, "0"}, // Value is "1" in the pref. {prefs::kServeAdAt, "[>]:7"}}; // 5 days ago in the pref. 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..70182f66e87f --- /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[] = "[=]:"; +constexpr char kGreaterThanOperatorConditionMatcherPrefix[] = "[>]:"; +constexpr char kGreaterThanOrEqualOperatorConditionMatcherPrefix[] = "[≥]:"; +constexpr char kLessThanOperatorConditionMatcherPrefix[] = "[<]:"; +constexpr char kLessThanOrEqualOperatorConditionMatcherPrefix[] = "[≤]:"; + +} // 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..7016fb02d842 --- /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*/, "[=]: 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*/, + "[=]: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*/, + "[=]: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*/, + "[>]: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*/, + "[>]: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*/, + "[≥]:1")); // Event occurred 2 days ago. + EXPECT_TRUE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[≥]: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*/, + "[≥]: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*/, + "[<]: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*/, + "[<]: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*/, + "[≤]:3")); // Event occurred 2 days ago. + EXPECT_TRUE( + MatchEpochOperator("13372214400000000" /*1st October 2024 00:00:00 UTC*/, + "[≤]: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*/, + "[≤]: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*/, "[_]: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..70f4be86754f --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.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/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 std::nullopt; + } + + return TimeDeltaSinceEpoch(static_cast(timestamp)); +} + +} // 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..ef2591251872 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/matchers/internal/epoch_operator_condition_matcher_util_internal.h @@ -0,0 +1,38 @@ +/* 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[] = "[?]:*"; + +// 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. +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..b0512d95f3fa --- /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,135 @@ +/* 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("[=]:-1")); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, ParseDayZero) { + // Act & Assert + EXPECT_EQ(0, ParseDays("[=]:0")); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, ParseDays) { + // Act & Assert + EXPECT_EQ(7, ParseDays("[=]:7")); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + DoNotParseNonIntegerDays) { + // Act & Assert + EXPECT_FALSE(ParseDays("[=]:1.5")); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + DoNotParseMalformedDays) { + // Act & Assert + EXPECT_FALSE(ParseDays("[=]: 7 ")); +} + +TEST_F(BraveAdsOperatorConditionMatcherUtilInternalTest, + DoNotParseInvalidDays) { + // Act & Assert + EXPECT_FALSE(ParseDays("[=]: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, DoNotParseTimeDelta) { + // Act & Assert + EXPECT_FALSE(ParseTimeDelta("broken time")); +} + +} // 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/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.cc b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.cc new file mode 100644 index 000000000000..88da962f65b6 --- /dev/null +++ b/components/brave_ads/core/internal/serving/targeting/condition_matcher/prefs/internal/condition_matcher_pref_util_internal.cc @@ -0,0 +1,178 @@ +/* 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 +#include + +#include "base/check.h" +#include "base/logging.h" +#include "base/notreached.h" +#include "base/strings/string_number_conversions.h" +#include "base/strings/string_split.h" +#include "base/types/cxx23_to_underlying.h" +#include "brave/components/brave_ads/core/public/prefs/pref_provider_interface.h" + +namespace brave_ads { + +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 MaybeGetRootPrefValue( + const PrefProviderInterface* const pref_provider, + const std::string& pref_path) { + CHECK(pref_provider); + + if (pref_path.starts_with(kVirtualPrefPathPrefix)) { + return pref_provider->GetVirtualPref(pref_path); + } + + if (std::optional pref_value = + pref_provider->GetProfilePref(pref_path)) { + return pref_value; + } + + if (std::optional pref_value = + pref_provider->GetLocalStatePref(pref_path)) { + return pref_value; + } + + // Unknown pref path. + return std::nullopt; +} + +std::optional MaybeGetDictPrefValue(const base::Value& pref_value, + const std::string& key) { + if (const base::Value* const value = pref_value.GetDict().Find(key)) { + return value->Clone(); + } + + // Unknown pref path key. + return std::nullopt; +} + +std::optional MaybeGetListPrefValue(const base::Value& pref_value, + const std::string& key) { + int index; + if (!base::StringToInt(key, &index)) { + // Invalid pref path key, because this should be an integer index for the + // list. + return std::nullopt; + } + + const base::Value::List& list = pref_value.GetList(); + + if (index < 0 || index >= static_cast(list.size())) { + // Invalid pref path key, because the list index is out of bounds. + return std::nullopt; + } + + return list[index].Clone(); +} + +std::optional MaybeGetNextPrefValue(const base::Value& pref_value, + const std::string& key) { + if (pref_value.is_dict()) { + return MaybeGetDictPrefValue(pref_value, key); + } + + if (pref_value.is_list()) { + return MaybeGetListPrefValue(pref_value, key); + } + + return std::nullopt; +} + +std::optional MaybeGetPrefValue( + const PrefProviderInterface* const pref_provider, + const std::string& pref_path) { + CHECK(pref_provider); + + // Split the `pref_path` into individual keys using '|' as the delimiter. + const std::vector keys = base::SplitString( + pref_path, "|", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); + if (keys.empty()) { + // Invalid pref path. + VLOG(1) << "Invalid condition matcher pref path: " << pref_path; + return std::nullopt; + } + + std::optional pref_value; + + for (auto iter = keys.cbegin(); iter != keys.cend(); ++iter) { + const std::string& key = *iter; + + if (!pref_value) { + // Attempt to get the root pref value using the current key. + if (std::optional root_pref_value = + MaybeGetRootPrefValue(pref_provider, key)) { + pref_value = std::move(*root_pref_value); + continue; + } + + // Unknown pref path key. + VLOG(1) << "Unknown condition matcher " << key << " key for " << pref_path + << " pref path"; + return std::nullopt; + } + + // Attempt to get the next pref value in the path. + pref_value = MaybeGetNextPrefValue(*pref_value, key); + if (!pref_value) { + // Unknown pref path key. + VLOG(1) << "Unknown condition matcher " << key << " key for " << pref_path + << " pref path"; + return std::nullopt; + } + + if (pref_value->is_dict() || pref_value->is_list()) { + // Continue iterating if the current pref value is a dictionary or list. + continue; + } + + if (iter != keys.cend() - 1) { + // Invalid pref path, because this should be the last pref path key. + VLOG(1) << "Invalid condition matcher " << key << " key for " << pref_path + << " pref path"; + return std::nullopt; + } + + break; + } + + // Return the last pref path value. + return pref_value; +} + +} // 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/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/targeting/condition_matcher/condition_matcher_util.h similarity index 76% rename from components/brave_ads/core/public/serving/new_tab_page_ad_serving_condition_matcher_util.h rename to components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h index 574671c9a44f..5faa95990aa1 100644 --- a/components/brave_ads/core/public/serving/new_tab_page_ad_serving_condition_matcher_util.h +++ b/components/brave_ads/core/public/serving/targeting/condition_matcher/condition_matcher_util.h @@ -3,25 +3,24 @@ * 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_ +#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 NewTabPageAdConditionMatcherMap = +using ConditionMatcherMap = 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 which contain dotted paths 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. +// 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 which contain dotted +// paths 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, @@ -107,9 +106,9 @@ class PrefProviderInterface; // } // ] // -// We support virtual preferences for values that are not persisted in the -// profile or local state preferences. Virtual preference path keys should be -// prefixed with "[virtual]:". +// We support virtual prefs for values that are not persisted in the profile or +// local state prefs. Virtual pref path keys should be prefixed with +// "[virtual]:". // // "[virtual]:operating_system.name" retrieves the operating system, returning // one of the following values: "Windows", "Mac OS X", "Linux", "Android", @@ -127,15 +126,15 @@ class PrefProviderInterface; // "Startpage", or "Ecosia". For the most up-to-date list of possible default // search engines, see `TemplateURLPrepopulateData::GetDefaultSearchEngine`. // -// 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. +// 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 NewTabPageAdConditionMatcherMap& condition_matchers); + const ConditionMatcherMap& condition_matchers); } // namespace brave_ads -#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_PUBLIC_SERVING_NEW_TAB_PAGE_AD_SERVING_CONDITION_MATCHER_UTIL_H_ +#endif // BRAVE_COMPONENTS_BRAVE_ADS_CORE_PUBLIC_SERVING_TARGETING_CONDITION_MATCHER_CONDITION_MATCHER_UTIL_H_ diff --git a/components/brave_ads/core/test/BUILD.gn b/components/brave_ads/core/test/BUILD.gn index 7c773f12b694..521c5cff0639 100644 --- a/components/brave_ads/core/test/BUILD.gn +++ b/components/brave_ads/core/test/BUILD.gn @@ -465,8 +465,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", @@ -528,6 +526,13 @@ 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/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 e9aeb933d7f7..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::NewTabPageAdConditionMatcherMap 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 4b01e760eee1..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::NewTabPageAdConditionMatcherMap 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 b4b53edb131e..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,8 +84,8 @@ class ViewCounterService : public KeyedService, std::optional GetCurrentWallpaperForDisplay(); std::optional GetCurrentWallpaper() const; std::optional GetCurrentBrandedWallpaper(); - std::optional - GetConditionMatchers(const base::Value::Dict& dict); + std::optional GetConditionMatchers( + const base::Value::Dict& dict); std::optional GetNextBrandedWallpaperWhichMatchesConditions(); std::optional GetCurrentBrandedWallpaperFromAdInfo() const;