Merge pull request #14540 from brave/issues/24529

Transition Brave Ads `StringPref`/`JSON` to `GetListPref` and `GetDictPref`
This commit is contained in:
Terry Mancey
2022-08-10 15:34:40 -05:00
committed by GitHub
33 changed files with 516 additions and 527 deletions
+4 -3
View File
@@ -67,9 +67,10 @@ void AdsService::RegisterProfilePrefs(
registry->RegisterIntegerPref(ads::prefs::kIssuerPing, 7'200'000);
registry->RegisterListPref(ads::prefs::kIssuers, base::Value::List());
registry->RegisterStringPref(ads::prefs::kEpsilonGreedyBanditArms, "");
registry->RegisterStringPref(ads::prefs::kEpsilonGreedyBanditEligibleSegments,
"");
registry->RegisterDictionaryPref(ads::prefs::kEpsilonGreedyBanditArms,
base::Value::Dict());
registry->RegisterListPref(ads::prefs::kEpsilonGreedyBanditEligibleSegments,
base::Value::List());
registry->RegisterTimePref(ads::prefs::kServeAdAt, base::Time());
+2 -4
View File
@@ -170,8 +170,6 @@ source_set("brave_ads_unit_tests") {
"//brave/vendor/bat-native-ads/src/bat/ads/internal/ads/serving/serving_features_unittest_util.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/ads/serving/serving_features_unittest_util.h",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/ads/serving/targeting/models/behavioral/bandits/epsilon_greedy_bandit_model_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/ads/serving/targeting/models/behavioral/bandits/epsilon_greedy_bandit_model_unittest_util.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/ads/serving/targeting/models/behavioral/bandits/epsilon_greedy_bandit_model_unittest_util.h",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/ads/serving/targeting/models/behavioral/purchase_intent/purchase_intent_model_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/ads/serving/targeting/models/contextual/text_classification/text_classification_model_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/ads/serving/targeting/top_segments_unittest.cc",
@@ -371,13 +369,13 @@ source_set("brave_ads_unit_tests") {
"//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/behavioral/anti_targeting/anti_targeting_features_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/behavioral/anti_targeting/anti_targeting_resource_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource_util_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/behavioral/conversions/conversions_resource_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/behavioral/purchase_intent/purchase_intent_resource_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/contextual/text_classification/text_classification_resource_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/resources/resource_manager_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/segments/segment_json_reader_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/segments/segment_json_writer_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/segments/segment_util_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/segments/segment_values_util_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/server/headers/via_header_util_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/server/url/hosts/anonymous_server_host_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/server/url/hosts/geo_server_host_unittest.cc",
+9 -6
View File
@@ -982,9 +982,12 @@ source_set("ads") {
"src/bat/ads/internal/processors/behavioral/bandits/bandit_feedback_info.h",
"src/bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_info.cc",
"src/bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_info.h",
"src/bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arms.cc",
"src/bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arms.h",
"src/bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_util.cc",
"src/bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_util.h",
"src/bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_values_util.cc",
"src/bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_values_util.h",
"src/bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arms_alias.h",
"src/bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_constants.h",
"src/bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_processor.cc",
"src/bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_processor.h",
"src/bat/ads/internal/processors/behavioral/purchase_intent/purchase_intent_processor.cc",
@@ -1001,6 +1004,8 @@ source_set("ads") {
"src/bat/ads/internal/resources/behavioral/anti_targeting/anti_targeting_resource.h",
"src/bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource.cc",
"src/bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource.h",
"src/bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource_util.cc",
"src/bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource_util.h",
"src/bat/ads/internal/resources/behavioral/conversions/conversion_id_pattern_info.cc",
"src/bat/ads/internal/resources/behavioral/conversions/conversion_id_pattern_info.h",
"src/bat/ads/internal/resources/behavioral/conversions/conversions_info.cc",
@@ -1028,12 +1033,10 @@ source_set("ads") {
"src/bat/ads/internal/resources/resources_util.h",
"src/bat/ads/internal/resources/resources_util_impl.h",
"src/bat/ads/internal/segments/segment_alias.h",
"src/bat/ads/internal/segments/segment_json_reader.cc",
"src/bat/ads/internal/segments/segment_json_reader.h",
"src/bat/ads/internal/segments/segment_json_writer.cc",
"src/bat/ads/internal/segments/segment_json_writer.h",
"src/bat/ads/internal/segments/segment_util.cc",
"src/bat/ads/internal/segments/segment_util.h",
"src/bat/ads/internal/segments/segment_values_util.cc",
"src/bat/ads/internal/segments/segment_values_util.h",
"src/bat/ads/internal/server/headers/via_header_util.cc",
"src/bat/ads/internal/server/headers/via_header_util.h",
"src/bat/ads/internal/server/url/hosts/anonymous_server_host.cc",
@@ -12,12 +12,11 @@
#include "base/containers/flat_map.h"
#include "base/rand_util.h"
#include "bat/ads/internal/ads_client_helper.h"
#include "bat/ads/internal/base/logging_util.h"
#include "bat/ads/internal/features/epsilon_greedy_bandit_features.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arms.h"
#include "bat/ads/internal/segments/segment_json_reader.h"
#include "bat/ads/pref_names.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_util.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arms_alias.h"
#include "bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource_util.h"
namespace ads {
namespace targeting {
@@ -69,25 +68,19 @@ ArmBucketMap BucketSortArms(const ArmList& arms) {
return buckets;
}
SegmentList GetEligibleSegments() {
const std::string json = AdsClientHelper::GetInstance()->GetStringPref(
prefs::kEpsilonGreedyBanditEligibleSegments);
return JSONReader::ReadSegments(json);
}
EpsilonGreedyBanditArmMap GetEligibleArms(
const EpsilonGreedyBanditArmMap& arms) {
const SegmentList eligible_segments = GetEligibleSegments();
if (eligible_segments.empty()) {
const SegmentList segments =
resource::GetEpsilonGreedyBanditEligibleSegments();
if (segments.empty()) {
return {};
}
EpsilonGreedyBanditArmMap eligible_arms;
for (const auto& arm : arms) {
if (std::find(eligible_segments.cbegin(), eligible_segments.cend(),
arm.first) == eligible_segments.end()) {
if (std::find(segments.cbegin(), segments.cend(), arm.first) ==
segments.end()) {
continue;
}
@@ -192,13 +185,7 @@ EpsilonGreedyBandit::EpsilonGreedyBandit() = default;
EpsilonGreedyBandit::~EpsilonGreedyBandit() = default;
SegmentList EpsilonGreedyBandit::GetSegments() const {
const std::string json = AdsClientHelper::GetInstance()->GetStringPref(
prefs::kEpsilonGreedyBanditArms);
const EpsilonGreedyBanditArmMap arms =
EpsilonGreedyBanditArms::FromJson(json);
return GetSegmentsForArms(arms);
return GetSegmentsForArms(GetEpsilonGreedyBanditArms());
}
} // namespace model
@@ -9,13 +9,14 @@
#include <vector>
#include "base/test/scoped_feature_list.h"
#include "bat/ads/internal/ads/serving/targeting/models/behavioral/bandits/epsilon_greedy_bandit_model_unittest_util.h"
#include "bat/ads/internal/base/unittest/unittest_base.h"
#include "bat/ads/internal/features/epsilon_greedy_bandit_features.h"
#include "bat/ads/internal/processors/behavioral/bandits/bandit_feedback_info.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_constants.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_processor.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_segments.h"
#include "bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource.h"
#include "bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource_util.h"
#include "bat/ads/internal/segments/segment_util.h"
// npm run test -- brave_unit_tests --filter=BatAds*
@@ -33,7 +34,7 @@ class BatAdsEpsilonGreedyBanditModelTest : public UnitTestBase {
TEST_F(BatAdsEpsilonGreedyBanditModelTest,
GetSegmentsIfProcessorNeverInitialized) {
// Arrange
SaveAllSegments();
resource::SetEpsilonGreedyBanditEligibleSegments(kSegments);
// Act
EpsilonGreedyBandit model;
@@ -61,7 +62,7 @@ TEST_F(BatAdsEpsilonGreedyBanditModelTest, EligableSegmentsAreEmpty) {
TEST_F(BatAdsEpsilonGreedyBanditModelTest, GetSegmentsIfNeverProcessed) {
// Arrange
SaveAllSegments();
resource::SetEpsilonGreedyBanditEligibleSegments(kSegments);
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
@@ -79,7 +80,7 @@ TEST_F(BatAdsEpsilonGreedyBanditModelTest, GetSegmentsIfNeverProcessed) {
TEST_F(BatAdsEpsilonGreedyBanditModelTest, GetSegmentsForExploration) {
// Arrange
SaveAllSegments();
resource::SetEpsilonGreedyBanditEligibleSegments(kSegments);
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
@@ -103,7 +104,7 @@ TEST_F(BatAdsEpsilonGreedyBanditModelTest, GetSegmentsForExploration) {
TEST_F(BatAdsEpsilonGreedyBanditModelTest, GetSegmentsForExploitation) {
// Arrange
SaveAllSegments();
resource::SetEpsilonGreedyBanditEligibleSegments(kSegments);
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
@@ -144,9 +145,8 @@ TEST_F(BatAdsEpsilonGreedyBanditModelTest, GetSegmentsForExploitation) {
TEST_F(BatAdsEpsilonGreedyBanditModelTest, GetSegmentsForEligibleSegments) {
// Arrange
const std::vector<std::string> eligible_segments = {
"science", "technology & computing", "invalid_segment"};
SaveSegments(eligible_segments);
resource::SetEpsilonGreedyBanditEligibleSegments(
{"science", "technology & computing", "invalid_segment"});
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndEnableFeatureWithParameters(
@@ -1,32 +0,0 @@
/* Copyright (c) 2021 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 http://mozilla.org/MPL/2.0/. */
#include "bat/ads/internal/ads/serving/targeting/models/behavioral/bandits/epsilon_greedy_bandit_model_unittest_util.h"
#include <string>
#include "bat/ads/internal/ads_client_helper.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_segments.h"
#include "bat/ads/internal/segments/segment_json_writer.h"
#include "bat/ads/pref_names.h"
namespace ads {
namespace targeting {
namespace model {
void SaveSegments(const SegmentList& segments) {
const std::string json = JSONWriter::WriteSegments(segments);
AdsClientHelper::GetInstance()->SetStringPref(
prefs::kEpsilonGreedyBanditEligibleSegments, json);
}
void SaveAllSegments() {
SaveSegments(kSegments);
}
} // namespace model
} // namespace targeting
} // namespace ads
@@ -1,23 +0,0 @@
/* Copyright (c) 2021 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 http://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_ADS_SERVING_TARGETING_MODELS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_MODEL_UNITTEST_UTIL_H_
#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_ADS_SERVING_TARGETING_MODELS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_MODEL_UNITTEST_UTIL_H_
#include "bat/ads/internal/segments/segment_alias.h"
namespace ads {
namespace targeting {
namespace model {
void SaveSegments(const SegmentList& segments);
void SaveAllSegments();
} // namespace model
} // namespace targeting
} // namespace ads
#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_ADS_SERVING_TARGETING_MODELS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_MODEL_UNITTEST_UTIL_H_
@@ -13,7 +13,6 @@
#include "base/strings/stringprintf.h"
#include "base/test/scoped_feature_list.h"
#include "bat/ads/internal/ads/serving/targeting/models/behavioral/bandits/epsilon_greedy_bandit_model_unittest_util.h"
#include "bat/ads/internal/ads/serving/targeting/user_model_builder.h"
#include "bat/ads/internal/ads/serving/targeting/user_model_info.h"
#include "bat/ads/internal/base/unittest/unittest_base.h"
@@ -21,13 +20,15 @@
#include "bat/ads/internal/features/purchase_intent_features.h"
#include "bat/ads/internal/features/text_classification_features.h"
#include "bat/ads/internal/processors/behavioral/bandits/bandit_feedback_info.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_constants.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_processor.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_segments.h"
#include "bat/ads/internal/processors/behavioral/purchase_intent/purchase_intent_processor.h"
#include "bat/ads/internal/processors/contextual/text_classification/text_classification_processor.h"
#include "bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource.h"
#include "bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource_util.h"
#include "bat/ads/internal/resources/behavioral/purchase_intent/purchase_intent_resource.h"
#include "bat/ads/internal/resources/contextual/text_classification/text_classification_resource.h"
#include "bat/ads/internal/segments/segment_util.h"
#include "url/gurl.h"
// npm run test -- brave_unit_tests --filter=BatAds*
@@ -154,7 +155,7 @@ class BatAdsTopSegmentsTest
TEST_P(BatAdsTopSegmentsTest, GetSegments) {
// Arrange
model::SaveAllSegments();
resource::SetEpsilonGreedyBanditEligibleSegments(kSegments);
ModelCombinationsParamInfo param(GetParam());
if (param.previously_processed) {
@@ -234,7 +235,7 @@ INSTANTIATE_TEST_SUITE_P(BatAdsTopSegmentsTest,
TEST_F(BatAdsTopSegmentsTest, GetSegmentsForAllModelsIfPreviouslyProcessed) {
// Arrange
model::SaveAllSegments();
resource::SetEpsilonGreedyBanditEligibleSegments(kSegments);
ProcessBandit();
ProcessTextClassification();
@@ -272,7 +273,7 @@ TEST_F(BatAdsTopSegmentsTest, GetSegmentsForAllModelsIfPreviouslyProcessed) {
TEST_F(BatAdsTopSegmentsTest, GetSegmentsForFieldTrialParticipationPath) {
// Arrange
model::SaveAllSegments();
resource::SetEpsilonGreedyBanditEligibleSegments(kSegments);
ProcessBandit();
ProcessTextClassification();
@@ -297,6 +297,11 @@ void UnitTestBase::SetDefaultPrefs() {
ads_client_mock_->SetInt64Pref(prefs::kIssuerPing, 0);
ads_client_mock_->SetListPref(prefs::kIssuers, base::Value::List());
ads_client_mock_->SetDictPref(prefs::kEpsilonGreedyBanditArms,
base::Value::Dict());
ads_client_mock_->SetListPref(prefs::kEpsilonGreedyBanditEligibleSegments,
base::Value::List());
ads_client_mock_->SetTimePref(prefs::kServeAdAt, Now());
ads_client_mock_->SetTimePref(prefs::kNextTokenRedemptionAt, DistantFuture());
@@ -0,0 +1,34 @@
/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_util.h"
#include "base/values.h"
#include "bat/ads/internal/ads_client_helper.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_values_util.h"
#include "bat/ads/pref_names.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace ads {
namespace targeting {
void SetEpsilonGreedyBanditArms(const EpsilonGreedyBanditArmMap& arms) {
AdsClientHelper::GetInstance()->SetDictPref(
prefs::kEpsilonGreedyBanditArms, EpsilonGreedyBanditArmsToValue(arms));
}
EpsilonGreedyBanditArmMap GetEpsilonGreedyBanditArms() {
const absl::optional<base::Value::Dict> dict =
AdsClientHelper::GetInstance()->GetDictPref(
prefs::kEpsilonGreedyBanditArms);
if (!dict) {
return {};
}
return EpsilonGreedyBanditArmsFromValue(*dict);
}
} // namespace targeting
} // namespace ads
@@ -0,0 +1,20 @@
/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_ARM_UTIL_H_
#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_ARM_UTIL_H_
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arms_alias.h"
namespace ads {
namespace targeting {
void SetEpsilonGreedyBanditArms(const EpsilonGreedyBanditArmMap& arms);
EpsilonGreedyBanditArmMap GetEpsilonGreedyBanditArms();
} // namespace targeting
} // namespace ads
#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_ARM_UTIL_H_
@@ -1,16 +1,14 @@
/* Copyright (c) 2020 The Brave Authors. All rights reserved.
/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arms.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_values_util.h"
#include <string>
#include <utility>
#include "base/check.h"
#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/values.h"
#include "bat/ads/internal/base/logging_util.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
@@ -23,67 +21,40 @@ constexpr char kSegmentKey[] = "segment";
constexpr char kValueKey[] = "value";
constexpr char kPullsKey[] = "pulls";
bool GetArmFromDictionary(const base::Value::Dict& dict,
EpsilonGreedyBanditArmInfo* info) {
DCHECK(info);
EpsilonGreedyBanditArmInfo arm;
absl::optional<EpsilonGreedyBanditArmInfo> MaybeGetArmFromDict(
const base::Value::Dict& dict) {
const std::string* segment = dict.FindString(kSegmentKey);
if (!segment || segment->empty()) {
return false;
return absl::nullopt;
}
EpsilonGreedyBanditArmInfo arm;
arm.segment = *segment;
arm.pulls = dict.FindInt(kPullsKey).value_or(0);
arm.value = dict.FindDouble(kValueKey).value_or(1.0);
*info = arm;
return true;
return arm;
}
EpsilonGreedyBanditArmMap GetArmsFromDictionary(const base::Value::Dict& dict) {
EpsilonGreedyBanditArmMap arms;
bool found_errors = false;
for (const auto [key, value] : dict) {
if (!value.is_dict()) {
found_errors = true;
continue;
}
const base::Value::Dict& arm_dict = value.GetDict();
EpsilonGreedyBanditArmInfo arm;
if (!GetArmFromDictionary(arm_dict, &arm)) {
found_errors = true;
continue;
}
arms[key] = arm;
absl::optional<EpsilonGreedyBanditArmInfo> MaybeGetArmFromValue(
const base::Value& value) {
const base::Value::Dict* dict = value.GetIfDict();
if (!dict) {
return absl::nullopt;
}
if (found_errors) {
BLOG(0, "Errors detected when parsing epsilon greedy bandit arms");
const absl::optional<EpsilonGreedyBanditArmInfo> arm =
MaybeGetArmFromDict(*dict);
if (!arm) {
return absl::nullopt;
}
return arms;
return arm;
}
} // namespace
EpsilonGreedyBanditArms::EpsilonGreedyBanditArms() = default;
EpsilonGreedyBanditArms::~EpsilonGreedyBanditArms() = default;
EpsilonGreedyBanditArmMap EpsilonGreedyBanditArms::FromJson(
const std::string& json) {
EpsilonGreedyBanditArmMap arms;
absl::optional<base::Value> value = base::JSONReader::Read(json);
if (!value || !value->is_dict()) {
return arms;
}
const base::Value::Dict& arm_dict = value->GetDict();
arms = GetArmsFromDictionary(arm_dict);
return arms;
}
std::string EpsilonGreedyBanditArms::ToJson(
base::Value::Dict EpsilonGreedyBanditArmsToValue(
const EpsilonGreedyBanditArmMap& arms) {
base::Value::Dict dict;
@@ -92,12 +63,35 @@ std::string EpsilonGreedyBanditArms::ToJson(
item.Set(kSegmentKey, key);
item.Set(kPullsKey, value.pulls);
item.Set(kValueKey, value.value);
dict.Set(key, std::move(item));
}
std::string json;
base::JSONWriter::Write(dict, &json);
return json;
return dict;
}
EpsilonGreedyBanditArmMap EpsilonGreedyBanditArmsFromValue(
const base::Value::Dict& dict) {
bool found_errors = false;
EpsilonGreedyBanditArmMap arms;
for (const auto [key, value] : dict) {
const absl::optional<EpsilonGreedyBanditArmInfo> arm =
MaybeGetArmFromValue(value);
if (!arm) {
found_errors = true;
continue;
}
arms[key] = *arm;
}
if (found_errors) {
BLOG(0, "Error parsing epsilon greedy bandit arms");
}
return arms;
}
} // namespace targeting
@@ -0,0 +1,23 @@
/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_ARM_VALUES_UTIL_H_
#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_ARM_VALUES_UTIL_H_
#include "base/values.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arms_alias.h"
namespace ads {
namespace targeting {
base::Value::Dict EpsilonGreedyBanditArmsToValue(
const EpsilonGreedyBanditArmMap& arms);
EpsilonGreedyBanditArmMap EpsilonGreedyBanditArmsFromValue(
const base::Value::Dict& dict);
} // namespace targeting
} // namespace ads
#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_ARM_VALUES_UTIL_H_
@@ -1,30 +0,0 @@
/* Copyright (c) 2020 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 http://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_ARMS_H_
#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_ARMS_H_
#include <string>
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arms_alias.h"
namespace ads {
namespace targeting {
class EpsilonGreedyBanditArms final {
public:
EpsilonGreedyBanditArms();
~EpsilonGreedyBanditArms();
EpsilonGreedyBanditArms(const EpsilonGreedyBanditArms&) = delete;
EpsilonGreedyBanditArms& operator=(const EpsilonGreedyBanditArms&) = delete;
static EpsilonGreedyBanditArmMap FromJson(const std::string& json);
static std::string ToJson(const EpsilonGreedyBanditArmMap& arms);
};
} // namespace targeting
} // namespace ads
#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_ARMS_H_
@@ -3,8 +3,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_SEGMENTS_H_
#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_SEGMENTS_H_
#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_CONSTANTS_H_
#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_CONSTANTS_H_
#include "bat/ads/internal/segments/segment_alias.h"
@@ -45,4 +45,4 @@ const SegmentList kSegments = {"architecture",
} // namespace targeting
} // namespace ads
#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_SEGMENTS_H_
#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_CONSTANTS_H_
@@ -7,23 +7,24 @@
#include <algorithm>
#include "base/check.h"
#include "base/check_op.h"
#include "base/notreached.h"
#include "bat/ads/internal/ads_client_helper.h"
#include "bat/ads/internal/base/logging_util.h"
#include "bat/ads/internal/processors/behavioral/bandits/bandit_feedback_info.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arms.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_segments.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_info.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_util.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_values_util.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arms_alias.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_constants.h"
#include "bat/ads/internal/segments/segment_util.h"
#include "bat/ads/pref_names.h"
namespace ads {
namespace processor {
namespace {
constexpr double kArmDefaultValue = 1.0;
constexpr uint64_t kArmDefaultPulls = 0;
constexpr double kDefaultArmValue = 1.0;
constexpr int kDefaultArmPulls = 0;
targeting::EpsilonGreedyBanditArmMap MaybeAddOrResetArms(
const targeting::EpsilonGreedyBanditArmMap& arms) {
@@ -42,8 +43,8 @@ targeting::EpsilonGreedyBanditArmMap MaybeAddOrResetArms(
}
targeting::EpsilonGreedyBanditArmInfo arm;
arm.value = kArmDefaultValue;
arm.pulls = kArmDefaultPulls;
arm.value = kDefaultArmValue;
arm.pulls = kDefaultArmPulls;
updated_arms[segment] = arm;
@@ -117,31 +118,22 @@ void EpsilonGreedyBandit::Process(const BanditFeedbackInfo& feedback) {
///////////////////////////////////////////////////////////////////////////////
void EpsilonGreedyBandit::InitializeArms() const {
std::string json = AdsClientHelper::GetInstance()->GetStringPref(
prefs::kEpsilonGreedyBanditArms);
targeting::EpsilonGreedyBanditArmMap arms =
targeting::EpsilonGreedyBanditArms::FromJson(json);
targeting::GetEpsilonGreedyBanditArms();
arms = MaybeAddOrResetArms(arms);
arms = MaybeDeleteArms(arms);
json = targeting::EpsilonGreedyBanditArms::ToJson(arms);
AdsClientHelper::GetInstance()->SetStringPref(prefs::kEpsilonGreedyBanditArms,
json);
targeting::SetEpsilonGreedyBanditArms(arms);
BLOG(1, "Successfully initialized epsilon greedy bandit arms");
}
void EpsilonGreedyBandit::UpdateArm(const uint64_t reward,
void EpsilonGreedyBandit::UpdateArm(const int reward,
const std::string& segment) const {
std::string json = AdsClientHelper::GetInstance()->GetStringPref(
prefs::kEpsilonGreedyBanditArms);
targeting::EpsilonGreedyBanditArmMap arms =
targeting::EpsilonGreedyBanditArms::FromJson(json);
targeting::GetEpsilonGreedyBanditArms();
if (arms.empty()) {
BLOG(1, "No epsilon greedy bandit arms");
return;
@@ -156,13 +148,12 @@ void EpsilonGreedyBandit::UpdateArm(const uint64_t reward,
targeting::EpsilonGreedyBanditArmInfo arm = iter->second;
arm.pulls++;
arm.value = arm.value + (1.0 / arm.pulls * (reward - arm.value));
DCHECK_NE(0, arm.pulls);
arm.value =
arm.value + (1.0 / arm.pulls * (static_cast<double>(reward) - arm.value));
iter->second = arm;
json = targeting::EpsilonGreedyBanditArms::ToJson(arms);
AdsClientHelper::GetInstance()->SetStringPref(prefs::kEpsilonGreedyBanditArms,
json);
targeting::SetEpsilonGreedyBanditArms(arms);
BLOG(1,
"Epsilon greedy bandit arm was updated for " << segment << " segment");
@@ -6,7 +6,6 @@
#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_PROCESSOR_H_
#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_PROCESSORS_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_PROCESSOR_H_
#include <cstdint>
#include <string>
namespace ads {
@@ -26,7 +25,7 @@ class EpsilonGreedyBandit final {
private:
void InitializeArms() const;
void UpdateArm(const uint64_t reward, const std::string& segment) const;
void UpdateArm(const int reward, const std::string& segment) const;
};
} // namespace processor
@@ -5,26 +5,12 @@
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_processor.h"
#include "bat/ads/internal/ads_client_helper.h"
#include "bat/ads/internal/base/containers/container_util.h"
#include "bat/ads/internal/base/unittest/unittest_base.h"
#include "bat/ads/internal/processors/behavioral/bandits/bandit_feedback_info.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arms.h"
#include "bat/ads/pref_names.h"
#include "bat/ads/internal/processors/behavioral/bandits/epsilon_greedy_bandit_arm_util.h"
// npm run test -- brave_unit_tests --filter=BatAds*
namespace {
constexpr char kArmsWithEmptySegmentJson[] = R"(
{
"travel":{"pulls":0,"segment":"travel","value":1.0},
"":{"pulls":0,"segment":"","value":1.0}
}
)";
} // namespace
namespace ads {
class BatAdsEpsilonGreedyBanditProcessorTest : public UnitTestBase {
@@ -34,50 +20,52 @@ class BatAdsEpsilonGreedyBanditProcessorTest : public UnitTestBase {
~BatAdsEpsilonGreedyBanditProcessorTest() override = default;
};
TEST_F(BatAdsEpsilonGreedyBanditProcessorTest, InitializeAllArmsFromResource) {
TEST_F(BatAdsEpsilonGreedyBanditProcessorTest, InitializeArmsFromResource) {
// Arrange
targeting::EpsilonGreedyBanditArmMap prefs_arms;
targeting::EpsilonGreedyBanditArmInfo prefs_arm_info;
prefs_arm_info.segment = "foo";
prefs_arms["foo"] = prefs_arm_info;
prefs_arm_info.segment = "bar";
prefs_arms["bar"] = prefs_arm_info;
{
targeting::EpsilonGreedyBanditArmMap arms;
AdsClientHelper::GetInstance()->SetStringPref(
prefs::kEpsilonGreedyBanditArms,
targeting::EpsilonGreedyBanditArms::ToJson(prefs_arms));
targeting::EpsilonGreedyBanditArmInfo arm_1;
arm_1.segment = "foo";
arm_1.pulls = 0;
arm_1.value = 1.0;
arms["foo"] = arm_1;
targeting::EpsilonGreedyBanditArmInfo arm_2;
arm_2.segment = "bar";
arm_2.pulls = 0;
arm_2.value = 1.0;
arms["bar"] = arm_2;
targeting::SetEpsilonGreedyBanditArms(arms);
}
// Act
processor::EpsilonGreedyBandit processor;
// Assert
std::string json = AdsClientHelper::GetInstance()->GetStringPref(
prefs::kEpsilonGreedyBanditArms);
targeting::EpsilonGreedyBanditArmMap arms =
targeting::EpsilonGreedyBanditArms::FromJson(json);
const targeting::EpsilonGreedyBanditArmMap arms =
targeting::GetEpsilonGreedyBanditArms();
EXPECT_EQ(30U, arms.size());
EXPECT_EQ(0u, arms.count("foo"));
EXPECT_EQ(0u, arms.count("bar"));
EXPECT_EQ(0U, arms.count("foo"));
EXPECT_EQ(0U, arms.count("bar"));
}
TEST_F(BatAdsEpsilonGreedyBanditProcessorTest, NeverProcessed) {
// Arrange
processor::EpsilonGreedyBandit processor;
const std::string segment = "travel"; // rewards: [] => value: 1.0
// Act
// rewards: [] => value: 1.0
std::string segment = "travel";
processor::EpsilonGreedyBandit processor;
// Assert
std::string json = AdsClientHelper::GetInstance()->GetStringPref(
prefs::kEpsilonGreedyBanditArms);
targeting::EpsilonGreedyBanditArmMap arms =
targeting::EpsilonGreedyBanditArms::FromJson(json);
const targeting::EpsilonGreedyBanditArmMap arms =
targeting::GetEpsilonGreedyBanditArms();
const auto iter = arms.find(segment);
ASSERT_TRUE(iter != arms.end());
const targeting::EpsilonGreedyBanditArmInfo arm = iter->second;
auto iter = arms.find(segment);
targeting::EpsilonGreedyBanditArmInfo arm = iter->second;
targeting::EpsilonGreedyBanditArmInfo expected_arm;
expected_arm.segment = segment;
expected_arm.value = 1.0;
@@ -89,24 +77,22 @@ TEST_F(BatAdsEpsilonGreedyBanditProcessorTest, NeverProcessed) {
TEST_F(BatAdsEpsilonGreedyBanditProcessorTest,
ProcessSegmentFourTimesWithOneReward) {
// Arrange
processor::EpsilonGreedyBandit processor;
const std::string segment = "travel"; // rewards: [0, 0, 0, 0] => value: 0.0
// Act
// rewards: [0, 0, 0, 0] => value: 0.0
std::string segment = "travel";
processor::EpsilonGreedyBandit processor;
processor.Process({segment, mojom::NotificationAdEventType::kDismissed});
processor.Process({segment, mojom::NotificationAdEventType::kDismissed});
processor.Process({segment, mojom::NotificationAdEventType::kTimedOut});
processor.Process({segment, mojom::NotificationAdEventType::kDismissed});
// Assert
std::string json = AdsClientHelper::GetInstance()->GetStringPref(
prefs::kEpsilonGreedyBanditArms);
targeting::EpsilonGreedyBanditArmMap arms =
targeting::EpsilonGreedyBanditArms::FromJson(json);
const targeting::EpsilonGreedyBanditArmMap arms =
targeting::GetEpsilonGreedyBanditArms();
const auto iter = arms.find(segment);
ASSERT_TRUE(iter != arms.end());
const targeting::EpsilonGreedyBanditArmInfo arm = iter->second;
auto iter = arms.find(segment);
targeting::EpsilonGreedyBanditArmInfo arm = iter->second;
targeting::EpsilonGreedyBanditArmInfo expected_arm;
expected_arm.segment = segment;
expected_arm.value = 0.0;
@@ -118,24 +104,22 @@ TEST_F(BatAdsEpsilonGreedyBanditProcessorTest,
TEST_F(BatAdsEpsilonGreedyBanditProcessorTest,
ProcessSegmentFourTimesWithTwoRewards) {
// Arrange
processor::EpsilonGreedyBandit processor;
const std::string segment = "travel"; // rewards: [1, 0, 1, 0] => value: 0.5
// Act
// rewards: [1, 0, 1, 0] => value: 0.5
std::string segment = "travel";
processor::EpsilonGreedyBandit processor;
processor.Process({segment, mojom::NotificationAdEventType::kClicked});
processor.Process({segment, mojom::NotificationAdEventType::kDismissed});
processor.Process({segment, mojom::NotificationAdEventType::kClicked});
processor.Process({segment, mojom::NotificationAdEventType::kTimedOut});
// Assert
std::string json = AdsClientHelper::GetInstance()->GetStringPref(
prefs::kEpsilonGreedyBanditArms);
targeting::EpsilonGreedyBanditArmMap arms =
targeting::EpsilonGreedyBanditArms::FromJson(json);
const targeting::EpsilonGreedyBanditArmMap arms =
targeting::GetEpsilonGreedyBanditArms();
const auto iter = arms.find(segment);
ASSERT_TRUE(iter != arms.end());
const targeting::EpsilonGreedyBanditArmInfo arm = iter->second;
auto iter = arms.find(segment);
targeting::EpsilonGreedyBanditArmInfo arm = iter->second;
targeting::EpsilonGreedyBanditArmInfo expected_arm;
expected_arm.segment = segment;
expected_arm.value = 0.5;
@@ -147,24 +131,22 @@ TEST_F(BatAdsEpsilonGreedyBanditProcessorTest,
TEST_F(BatAdsEpsilonGreedyBanditProcessorTest,
ProcessSegmentFourTimesWithFourRewards) {
// Arrange
processor::EpsilonGreedyBandit processor;
const std::string segment = "travel"; // rewards: [1, 1, 1, 1] => value: 1.0
// Act
// rewards: [1, 1, 1, 1] => value: 1.0
std::string segment = "travel";
processor::EpsilonGreedyBandit processor;
processor.Process({segment, mojom::NotificationAdEventType::kClicked});
processor.Process({segment, mojom::NotificationAdEventType::kClicked});
processor.Process({segment, mojom::NotificationAdEventType::kClicked});
processor.Process({segment, mojom::NotificationAdEventType::kClicked});
// Assert
std::string json = AdsClientHelper::GetInstance()->GetStringPref(
prefs::kEpsilonGreedyBanditArms);
targeting::EpsilonGreedyBanditArmMap arms =
targeting::EpsilonGreedyBanditArms::FromJson(json);
const targeting::EpsilonGreedyBanditArmMap arms =
targeting::GetEpsilonGreedyBanditArms();
const auto iter = arms.find(segment);
ASSERT_TRUE(iter != arms.end());
const targeting::EpsilonGreedyBanditArmInfo arm = iter->second;
auto iter = arms.find(segment);
targeting::EpsilonGreedyBanditArmInfo arm = iter->second;
targeting::EpsilonGreedyBanditArmInfo expected_arm;
expected_arm.segment = segment;
expected_arm.value = 1.0;
@@ -175,39 +157,35 @@ TEST_F(BatAdsEpsilonGreedyBanditProcessorTest,
TEST_F(BatAdsEpsilonGreedyBanditProcessorTest, ProcessSegmentNotInResource) {
// Arrange
processor::EpsilonGreedyBandit processor;
const std::string segment = "foobar";
// Act
std::string segment = "foobar";
processor::EpsilonGreedyBandit processor;
processor.Process({segment, mojom::NotificationAdEventType::kTimedOut});
// Assert
std::string json = AdsClientHelper::GetInstance()->GetStringPref(
prefs::kEpsilonGreedyBanditArms);
targeting::EpsilonGreedyBanditArmMap arms =
targeting::EpsilonGreedyBanditArms::FromJson(json);
auto iter = arms.find(segment);
const targeting::EpsilonGreedyBanditArmMap arms =
targeting::GetEpsilonGreedyBanditArms();
const auto iter = arms.find(segment);
EXPECT_TRUE(iter == arms.end());
}
TEST_F(BatAdsEpsilonGreedyBanditProcessorTest, ProcessChildSegment) {
// Arrange
processor::EpsilonGreedyBandit processor;
const std::string segment = "travel-child";
const std::string parent_segment = "travel";
// Act
std::string segment = "travel-child";
std::string parent_segment = "travel";
processor::EpsilonGreedyBandit processor;
processor.Process({segment, mojom::NotificationAdEventType::kTimedOut});
// Assert
std::string json = AdsClientHelper::GetInstance()->GetStringPref(
prefs::kEpsilonGreedyBanditArms);
targeting::EpsilonGreedyBanditArmMap arms =
targeting::EpsilonGreedyBanditArms::FromJson(json);
const targeting::EpsilonGreedyBanditArmMap arms =
targeting::GetEpsilonGreedyBanditArms();
const auto iter = arms.find(parent_segment);
ASSERT_TRUE(iter != arms.end());
const targeting::EpsilonGreedyBanditArmInfo arm = iter->second;
auto iter = arms.find(parent_segment);
targeting::EpsilonGreedyBanditArmInfo arm = iter->second;
targeting::EpsilonGreedyBanditArmInfo expected_arm;
expected_arm.segment = parent_segment;
expected_arm.value = 0.0;
@@ -219,13 +197,29 @@ TEST_F(BatAdsEpsilonGreedyBanditProcessorTest, ProcessChildSegment) {
TEST_F(BatAdsEpsilonGreedyBanditProcessorTest,
InitializeArmsFromResourceWithEmptySegments) {
// Arrange
{
targeting::EpsilonGreedyBanditArmMap arms;
targeting::EpsilonGreedyBanditArmInfo arm_1;
arm_1.segment = "travel";
arm_1.pulls = 0;
arm_1.value = 1.0;
arms["travel"] = arm_1;
targeting::EpsilonGreedyBanditArmInfo arm_2;
arm_2.segment = "";
arm_2.pulls = 0;
arm_2.value = 1.0;
arms[""] = arm_2;
targeting::SetEpsilonGreedyBanditArms(arms);
}
// Act
const targeting::EpsilonGreedyBanditArmMap arms =
targeting::EpsilonGreedyBanditArms::FromJson(kArmsWithEmptySegmentJson);
targeting::GetEpsilonGreedyBanditArms();
// Assert
// Empty segments are skipped.
EXPECT_EQ(1U, arms.size());
EXPECT_EQ(1U, arms.count("travel"));
}
@@ -5,32 +5,16 @@
#include "bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource.h"
#include <string>
#include "base/check.h"
#include "bat/ads/internal/ads_client_helper.h"
#include "bat/ads/internal/base/logging_util.h"
#include "bat/ads/internal/catalog/catalog.h"
#include "bat/ads/internal/catalog/catalog_info.h"
#include "bat/ads/internal/segments/segment_json_reader.h"
#include "bat/ads/internal/segments/segment_json_writer.h"
#include "bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource_util.h"
#include "bat/ads/internal/segments/segment_util.h"
#include "bat/ads/pref_names.h"
namespace ads {
namespace resource {
namespace {
SegmentList GetSegments() {
const std::string json = AdsClientHelper::GetInstance()->GetStringPref(
prefs::kEpsilonGreedyBanditEligibleSegments);
return JSONReader::ReadSegments(json);
}
} // namespace
EpsilonGreedyBandit::EpsilonGreedyBandit(Catalog* catalog) : catalog_(catalog) {
DCHECK(catalog_);
@@ -48,21 +32,19 @@ bool EpsilonGreedyBandit::IsInitialized() const {
void EpsilonGreedyBandit::LoadFromCatalog(const CatalogInfo& catalog) {
const SegmentList segments = GetSegments(catalog);
const SegmentList parent_segments = GetParentSegments(segments);
const std::string json = JSONWriter::WriteSegments(parent_segments);
AdsClientHelper::GetInstance()->SetStringPref(
prefs::kEpsilonGreedyBanditEligibleSegments, json);
BLOG(2, "Successfully loaded epsilon greedy bandit segments:");
for (const auto& segment : parent_segments) {
BLOG(2, " " << segment);
}
SetEpsilonGreedyBanditEligibleSegments(parent_segments);
is_initialized_ = true;
}
SegmentList EpsilonGreedyBandit::Get() const {
return GetSegments();
return GetEpsilonGreedyBanditEligibleSegments();
}
///////////////////////////////////////////////////////////////////////////////
@@ -0,0 +1,34 @@
/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */
#include "bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource_util.h"
#include "base/values.h"
#include "bat/ads/internal/ads_client_helper.h"
#include "bat/ads/internal/segments/segment_values_util.h"
#include "bat/ads/pref_names.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace ads {
namespace resource {
void SetEpsilonGreedyBanditEligibleSegments(const SegmentList& segments) {
AdsClientHelper::GetInstance()->SetListPref(
prefs::kEpsilonGreedyBanditEligibleSegments, SegmentsToValue(segments));
}
SegmentList GetEpsilonGreedyBanditEligibleSegments() {
const absl::optional<base::Value::List> list =
AdsClientHelper::GetInstance()->GetListPref(
prefs::kEpsilonGreedyBanditEligibleSegments);
if (!list) {
return {};
}
return SegmentsFromValue(*list);
}
} // namespace resource
} // namespace ads
@@ -0,0 +1,20 @@
/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_RESOURCES_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_RESOURCE_UTIL_H_
#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_RESOURCES_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_RESOURCE_UTIL_H_
#include "bat/ads/internal/segments/segment_alias.h"
namespace ads {
namespace resource {
void SetEpsilonGreedyBanditEligibleSegments(const SegmentList& segments);
SegmentList GetEpsilonGreedyBanditEligibleSegments();
} // namespace resource
} // namespace ads
#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_RESOURCES_BEHAVIORAL_BANDITS_EPSILON_GREEDY_BANDIT_RESOURCE_UTIL_H_
@@ -0,0 +1,49 @@
/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */
#include "bat/ads/internal/resources/behavioral/bandits/epsilon_greedy_bandit_resource_util.h"
#include "bat/ads/internal/base/unittest/unittest_base.h"
#include "bat/ads/internal/segments/segment_alias.h"
// npm run test -- brave_unit_tests --filter=BatAds*
namespace ads {
namespace resource {
namespace {
const SegmentList kSegments = {"foo", "bar"};
} // namespace
class BatAdsEpsilonGreedyBanditResourceUtilTest : public UnitTestBase {
protected:
BatAdsEpsilonGreedyBanditResourceUtilTest() = default;
~BatAdsEpsilonGreedyBanditResourceUtilTest() override = default;
};
TEST_F(BatAdsEpsilonGreedyBanditResourceUtilTest, SetEligibleSegments) {
// Arrange
// Act
SetEpsilonGreedyBanditEligibleSegments(kSegments);
// Assert
EXPECT_EQ(kSegments, GetEpsilonGreedyBanditEligibleSegments());
}
TEST_F(BatAdsEpsilonGreedyBanditResourceUtilTest, SetNoEligibleSegments) {
// Arrange
// Act
SetEpsilonGreedyBanditEligibleSegments({});
// Assert
const SegmentList segments = GetEpsilonGreedyBanditEligibleSegments();
EXPECT_TRUE(segments.empty());
}
} // namespace resource
} // namespace ads
@@ -1,44 +0,0 @@
/* Copyright (c) 2021 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 http://mozilla.org/MPL/2.0/. */
#include "bat/ads/internal/segments/segment_json_reader.h"
#include "base/json/json_reader.h"
#include "base/values.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace ads {
namespace JSONReader {
SegmentList ReadSegments(const std::string& json) {
absl::optional<base::Value> value = base::JSONReader::Read(json);
if (!value) {
return {};
}
base::ListValue* list = nullptr;
if (!value->GetAsList(&list)) {
return {};
}
SegmentList segments;
for (const auto& element : list->GetList()) {
if (!element.is_string()) {
return {};
}
const std::string segment = element.GetString();
if (segment.empty()) {
return {};
}
segments.push_back(segment);
}
return segments;
}
} // namespace JSONReader
} // namespace ads
@@ -1,21 +0,0 @@
/* Copyright (c) 2021 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 http://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_SEGMENTS_SEGMENT_JSON_READER_H_
#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_SEGMENTS_SEGMENT_JSON_READER_H_
#include <string>
#include "bat/ads/internal/segments/segment_alias.h"
namespace ads {
namespace JSONReader {
SegmentList ReadSegments(const std::string& json);
} // namespace JSONReader
} // namespace ads
#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_SEGMENTS_SEGMENT_JSON_READER_H_
@@ -1,43 +0,0 @@
/* Copyright (c) 2021 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 http://mozilla.org/MPL/2.0/. */
#include "bat/ads/internal/segments/segment_json_reader.h"
#include "testing/gtest/include/gtest/gtest.h"
// npm run test -- brave_unit_tests --filter=BatAds*
namespace ads {
namespace JSONReader {
TEST(BatAdsSegmentsJsonReaderTest, ValidJson) {
// Arrange
const std::string json =
R"(["technology & computing","personal finance-banking","food & drink-restaurants"])";
// Act
const SegmentList segments = ReadSegments(json);
// Assert
const SegmentList expected_segments = {"technology & computing",
"personal finance-banking",
"food & drink-restaurants"};
EXPECT_EQ(expected_segments, segments);
}
TEST(BatAdsSegmentsJsonReaderTest, InvalidJson) {
// Arrange
const std::string json = R"({FOOBAR})";
// Act
const SegmentList segments = ReadSegments(json);
// Assert
const SegmentList expected_segments = {};
EXPECT_EQ(expected_segments, segments);
}
} // namespace JSONReader
} // namespace ads
@@ -1,30 +0,0 @@
/* Copyright (c) 2021 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 http://mozilla.org/MPL/2.0/. */
#include "bat/ads/internal/segments/segment_json_writer.h"
#include "base/check.h"
#include "base/json/json_writer.h"
#include "base/values.h"
namespace ads {
namespace JSONWriter {
std::string WriteSegments(const SegmentList& segments) {
base::Value list(base::Value::Type::LIST);
for (const auto& segment : segments) {
DCHECK(!segment.empty());
list.Append(segment);
}
std::string json;
base::JSONWriter::Write(list, &json);
return json;
}
} // namespace JSONWriter
} // namespace ads
@@ -1,43 +0,0 @@
/* Copyright (c) 2021 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 http://mozilla.org/MPL/2.0/. */
#include "bat/ads/internal/segments/segment_json_writer.h"
#include "testing/gtest/include/gtest/gtest.h"
// npm run test -- brave_unit_tests --filter=BatAds*
namespace ads {
namespace JSONWriter {
TEST(BatAdsSegmentsJsonWriterTest, Segments) {
// Arrange
const SegmentList segments = {"technology & computing",
"personal finance-banking",
"food & drink-restaurants"};
// Act
const std::string json = WriteSegments(segments);
// Assert
const std::string expected_json =
R"(["technology & computing","personal finance-banking","food & drink-restaurants"])";
EXPECT_EQ(expected_json, json);
}
TEST(BatAdsSegmentsJsonWriterTest, NoSegments) {
// Arrange
const SegmentList segments = {};
// Act
const std::string json = WriteSegments(segments);
// Assert
const std::string expected_json = R"([])";
EXPECT_EQ(expected_json, json);
}
} // namespace JSONWriter
} // namespace ads
@@ -32,8 +32,8 @@ SegmentList GetSegments(const T& creative_ads) {
}
std::string GetParentSegment(const std::string& segment);
bool MatchParentSegments(const std::string& lhs, const std::string& rhs);
bool MatchParentSegments(const std::string& lhs, const std::string& rhs);
SegmentList GetParentSegments(const SegmentList& segments);
bool HasChildSegment(const std::string& segment);
@@ -5,8 +5,6 @@
#include "bat/ads/internal/segments/segment_util.h"
#include <string>
#include "bat/ads/internal/base/unittest/unittest_base.h"
#include "bat/ads/internal/base/unittest/unittest_file_util.h"
#include "bat/ads/internal/catalog/catalog_info.h"
@@ -0,0 +1,44 @@
/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */
#include "bat/ads/internal/segments/segment_values_util.h"
#include <string>
#include "base/check.h"
namespace ads {
base::Value::List SegmentsToValue(const SegmentList& segments) {
base::Value::List list;
for (const auto& segment : segments) {
DCHECK(!segment.empty());
list.Append(segment);
}
return list;
}
SegmentList SegmentsFromValue(const base::Value::List& value) {
SegmentList segments;
for (const auto& item : value) {
if (!item.is_string()) {
return {};
}
const std::string& segment = item.GetString();
if (segment.empty()) {
return {};
}
segments.push_back(segment);
}
return segments;
}
} // namespace ads
@@ -1,21 +1,19 @@
/* Copyright (c) 2021 The Brave Authors. All rights reserved.
/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */
#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_SEGMENTS_SEGMENT_JSON_WRITER_H_
#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_SEGMENTS_SEGMENT_JSON_WRITER_H_
#include <string>
#ifndef BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_SEGMENTS_SEGMENT_VALUES_UTIL_H_
#define BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_SEGMENTS_SEGMENT_VALUES_UTIL_H_
#include "base/values.h"
#include "bat/ads/internal/segments/segment_alias.h"
namespace ads {
namespace JSONWriter {
std::string WriteSegments(const SegmentList& segments);
base::Value::List SegmentsToValue(const SegmentList& segments);
SegmentList SegmentsFromValue(const base::Value::List& value);
} // namespace JSONWriter
} // namespace ads
#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_SEGMENTS_SEGMENT_JSON_WRITER_H_
#endif // BRAVE_VENDOR_BAT_NATIVE_ADS_SRC_BAT_ADS_INTERNAL_SEGMENTS_SEGMENT_VALUES_UTIL_H_
@@ -0,0 +1,82 @@
/* Copyright (c) 2022 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 http://mozilla.org/MPL/2.0/. */
#include "bat/ads/internal/segments/segment_values_util.h"
#include "base/test/values_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
// npm run test -- brave_unit_tests --filter=BatAds*
namespace ads {
namespace {
constexpr char kSegmentsAsJson[] =
R"(["technology & computing","personal finance-banking","food & drink-restaurants"])";
constexpr char kNoSegmentsAsJson[] = "[]";
} // namespace
TEST(BatAdsSegmentValuesUtilTest, SegmentsToValue) {
// Arrange
// Act
const base::Value::List list =
SegmentsToValue({"technology & computing", "personal finance-banking",
"food & drink-restaurants"});
// Assert
const base::Value value = base::test::ParseJson(kSegmentsAsJson);
const base::Value::List* expected_list = value.GetIfList();
ASSERT_TRUE(expected_list);
EXPECT_EQ(*expected_list, list);
}
TEST(BatAdsSegmentValuesUtilTest, NoSegmentsToValue) {
// Arrange
// Act
const base::Value::List list = SegmentsToValue({});
// Assert
const base::Value value = base::test::ParseJson(kNoSegmentsAsJson);
const base::Value::List* expected_list = value.GetIfList();
ASSERT_TRUE(expected_list);
EXPECT_EQ(*expected_list, list);
}
TEST(BatAdsSegmentValuesUtilTest, SegmentsFromValue) {
// Arrange
const base::Value value = base::test::ParseJson(kSegmentsAsJson);
const base::Value::List* list = value.GetIfList();
ASSERT_TRUE(list);
// Act
const SegmentList segments = SegmentsFromValue(*list);
// Assert
const SegmentList expected_segments = {"technology & computing",
"personal finance-banking",
"food & drink-restaurants"};
EXPECT_EQ(expected_segments, segments);
}
TEST(BatAdsSegmentValuesUtilTest, NoSegmentsFromValue) {
// Arrange
const base::Value value = base::test::ParseJson(kNoSegmentsAsJson);
const base::Value::List* list = value.GetIfList();
ASSERT_TRUE(list);
// Act
const SegmentList segments = SegmentsFromValue(*list);
// Assert
EXPECT_TRUE(segments.empty());
}
} // namespace ads
+3 -5
View File
@@ -49,13 +49,11 @@ const char kCatalogLastUpdated[] = "brave.brave_ads.catalog_last_updated";
const char kIssuerPing[] = "brave.brave_ads.issuer_ping";
const char kIssuers[] = "brave.brave_ads.issuers";
// Stores epsilon greedy bandit arms
// Stores epsilon greedy bandit
const char kEpsilonGreedyBanditArms[] =
"brave.brave_ads.epsilon_greedy_bandit_arms";
// Stores epsilon greedy bandit eligible segments
"brave.brave_ads.epsilon_greedy_bandit_arms.v2";
const char kEpsilonGreedyBanditEligibleSegments[] =
"brave.brave_ads.epsilon_greedy_bandit_eligible_segments";
"brave.brave_ads.epsilon_greedy_bandit_eligible_segments.v2";
// Ads
const char kServeAdAt[] = "brave.brave_ads.serve_ad_at";