Skip to content

Commit

Permalink
Refactors Frequency Capping out from AdsImpl for unit testing. Introd…
Browse files Browse the repository at this point in the history
…uces 28 new unit tests, and two bug fixes related to the same ticket.

Resolves brave/brave-browser#4207
  • Loading branch information
masparrow committed Oct 29, 2019
1 parent 22a8916 commit 9bdac46
Show file tree
Hide file tree
Showing 30 changed files with 1,843 additions and 238 deletions.
8 changes: 8 additions & 0 deletions test/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,14 @@ test("brave_unit_tests") {
"//brave/vendor/bat-native-confirmations/src/bat/confirmations/internal/confirmations_client_mock.cc",
"//brave/vendor/bat-native-confirmations/src/bat/confirmations/internal/confirmations_client_mock.h",
"//brave/vendor/bat-native-usermodel/test/usermodel_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/client_mock.h",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/client_mock.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/frequency_capping/exclusion_rules/daily_cap_frequency_cap_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/frequency_capping/exclusion_rules/per_day_frequency_cap_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/frequency_capping/exclusion_rules/per_hour_frequency_cap_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/frequency_capping/exclusion_rules/total_max_frequency_cap_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/frequency_capping/permission_rules/minimum_wait_time_frequency_cap_unittest.cc",
"//brave/vendor/bat-native-ads/src/bat/ads/internal/frequency_capping/permission_rules/per_day_limit_frequency_cap_unittest.cc",
]
}

Expand Down
16 changes: 16 additions & 0 deletions vendor/bat-native-ads/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,22 @@ source_set("ads") {
"src/bat/ads/internal/filtered_category.h",
"src/bat/ads/internal/flagged_ad.cc",
"src/bat/ads/internal/flagged_ad.h",
"src/bat/ads/internal/frequency_capping/exclusion_rule.h",
"src/bat/ads/internal/frequency_capping/exclusion_rules/daily_cap_frequency_cap.cc",
"src/bat/ads/internal/frequency_capping/exclusion_rules/daily_cap_frequency_cap.h",
"src/bat/ads/internal/frequency_capping/exclusion_rules/per_day_frequency_cap.cc",
"src/bat/ads/internal/frequency_capping/exclusion_rules/per_day_frequency_cap.h",
"src/bat/ads/internal/frequency_capping/exclusion_rules/per_hour_frequency_cap.cc",
"src/bat/ads/internal/frequency_capping/exclusion_rules/per_hour_frequency_cap.h",
"src/bat/ads/internal/frequency_capping/exclusion_rules/total_max_frequency_cap.cc",
"src/bat/ads/internal/frequency_capping/exclusion_rules/total_max_frequency_cap.h",
"src/bat/ads/internal/frequency_capping/frequency_capping.cc",
"src/bat/ads/internal/frequency_capping/frequency_capping.h",
"src/bat/ads/internal/frequency_capping/permission_rule.h",
"src/bat/ads/internal/frequency_capping/permission_rules/minimum_wait_time_frequency_cap.cc",
"src/bat/ads/internal/frequency_capping/permission_rules/minimum_wait_time_frequency_cap.h",
"src/bat/ads/internal/frequency_capping/permission_rules/per_day_limit_frequency_cap.cc",
"src/bat/ads/internal/frequency_capping/permission_rules/per_day_limit_frequency_cap.h",
"src/bat/ads/internal/json_helper.cc",
"src/bat/ads/internal/json_helper.h",
"src/bat/ads/internal/locale_helper.cc",
Expand Down
263 changes: 69 additions & 194 deletions vendor/bat-native-ads/src/bat/ads/internal/ads_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@
#include "bat/ads/internal/static_values.h"
#include "bat/ads/internal/time.h"
#include "bat/ads/internal/uri_helper.h"
#include "bat/ads/internal/frequency_capping/exclusion_rule.h"
#include "bat/ads/internal/frequency_capping/frequency_capping.h"
#include "bat/ads/internal/frequency_capping/exclusion_rules/per_hour_frequency_cap.h"
#include "bat/ads/internal/frequency_capping/exclusion_rules/per_day_frequency_cap.h"
#include "bat/ads/internal/frequency_capping/exclusion_rules/daily_cap_frequency_cap.h"
#include "bat/ads/internal/frequency_capping/exclusion_rules/total_max_frequency_cap.h"
#include "bat/ads/internal/frequency_capping/permission_rules/minimum_wait_time_frequency_cap.h"
#include "bat/ads/internal/frequency_capping/permission_rules/per_day_limit_frequency_cap.h"

#include "rapidjson/document.h"
#include "rapidjson/error/en.h"
Expand Down Expand Up @@ -1042,39 +1050,48 @@ void AdsImpl::ServeAd(
ShowAd(ad, category);
}

std::vector<AdInfo> AdsImpl::GetEligibleAds(
const std::vector<AdInfo>& ads) {
std::vector<AdInfo> eligible_ads = {};
std::vector<ExclusionRule*> AdsImpl::CreateExclusionRules() const {
auto frequency_capping = std::make_unique<FrequencyCapping>(client_.get());

auto unseen_ads = GetUnseenAdsAndRoundRobinIfNeeded(ads);
std::vector<ExclusionRule*> exclusion_rules;

for (const auto& ad : unseen_ads) {
if (!AdRespectsTotalMaxFrequencyCapping(ad)) {
BLOG(WARNING) << "creativeSetId " << ad.creative_set_id
<< " has exceeded the frequency capping for totalMax";
auto daily_cap_frequency_cap = std::make_unique<DailyCapFrequencyCap>
(frequency_capping.get());
DCHECK(daily_cap_frequency_cap);
exclusion_rules.push_back(daily_cap_frequency_cap.get());

continue;
}
auto per_day_frequency_cap = std::make_unique<PerDayFrequencyCap>
(frequency_capping.get());
DCHECK(per_day_frequency_cap);
exclusion_rules.push_back(per_day_frequency_cap.get());

if (!AdRespectsPerHourFrequencyCapping(ad)) {
BLOG(WARNING) << "adUUID " << ad.uuid
<< " has exceeded the frequency capping for perHour";
auto per_hour_frequency_cap = std::make_unique<PerHourFrequencyCap>
(frequency_capping.get());
DCHECK(per_hour_frequency_cap);
exclusion_rules.push_back(per_hour_frequency_cap.get());

continue;
}
auto total_max_frequency_cap = std::make_unique<TotalMaxFrequencyCap>
(frequency_capping.get());
DCHECK(total_max_frequency_cap);
exclusion_rules.push_back(total_max_frequency_cap.get());

if (!AdRespectsPerDayFrequencyCapping(ad)) {
BLOG(WARNING) << "creativeSetId " << ad.creative_set_id
<< " has exceeded the frequency capping for perDay";
return exclusion_rules;
}

continue;
}
std::vector<AdInfo> AdsImpl::GetEligibleAds(
const std::vector<AdInfo>& ads) {
std::vector<AdInfo> eligible_ads = {};

if (!AdRespectsDailyCapFrequencyCapping(ad)) {
BLOG(WARNING) << "campaignId " << ad.campaign_id
<< " has exceeded the frequency capping for dailyCap";
auto unseen_ads = GetUnseenAdsAndRoundRobinIfNeeded(ads);

continue;
auto exclusion_rules = CreateExclusionRules();

for (const auto& ad : unseen_ads) {
for (ExclusionRule* exclusion_rule : exclusion_rules) {
if (exclusion_rule->ShouldExclude(ad)) {
BLOG(WARNING) << exclusion_rule->GetLastMessage();
continue;
}
}

if (client_->IsFilteredAd(ad.creative_set_id)) {
Expand Down Expand Up @@ -1124,81 +1141,6 @@ std::vector<AdInfo> AdsImpl::GetUnseenAds(
return unseen_ads;
}

bool AdsImpl::AdRespectsTotalMaxFrequencyCapping(
const AdInfo& ad) {
auto creative_set = GetCreativeSetForId(ad.creative_set_id);
if (creative_set.size() >= ad.total_max) {
return false;
}

return true;
}

bool AdsImpl::AdRespectsPerHourFrequencyCapping(
const AdInfo& ad) {
auto ads_shown = GetAdsShownForId(ad.uuid);
auto hour_window = base::Time::kSecondsPerHour;

return HistoryRespectsRollingTimeConstraint(
ads_shown, hour_window, 1);
}

bool AdsImpl::AdRespectsPerDayFrequencyCapping(
const AdInfo& ad) {
auto creative_set = GetCreativeSetForId(ad.creative_set_id);
auto day_window = base::Time::kSecondsPerHour * base::Time::kHoursPerDay;

return HistoryRespectsRollingTimeConstraint(
creative_set, day_window, ad.per_day);
}

bool AdsImpl::AdRespectsDailyCapFrequencyCapping(
const AdInfo& ad) {
auto campaign = GetCampaignForId(ad.campaign_id);
auto day_window = base::Time::kSecondsPerHour * base::Time::kHoursPerDay;

return HistoryRespectsRollingTimeConstraint(
campaign, day_window, ad.daily_cap);
}

std::deque<uint64_t> AdsImpl::GetAdsShownForId(
const std::string& id) {
std::deque<uint64_t> ads_shown = {};

auto ads_shown_history = client_->GetAdsShownHistory();
for (const auto& ad_shown : ads_shown_history) {
if (ad_shown.ad_content.uuid == id) {
ads_shown.push_back(ad_shown.timestamp_in_seconds);
}
}

return ads_shown;
}

std::deque<uint64_t> AdsImpl::GetCreativeSetForId(
const std::string& id) {
std::deque<uint64_t> creative_set = {};

auto creative_set_history = client_->GetCreativeSetHistory();
if (creative_set_history.find(id) != creative_set_history.end()) {
creative_set = creative_set_history.at(id);
}

return creative_set;
}

std::deque<uint64_t> AdsImpl::GetCampaignForId(
const std::string& id) {
std::deque<uint64_t> campaign = {};

auto campaign_history = client_->GetCampaignHistory();
if (campaign_history.find(id) != campaign_history.end()) {
campaign = campaign_history.at(id);
}

return campaign;
}

bool AdsImpl::IsAdValid(
const AdInfo& ad_info) {
if (ad_info.advertiser.empty() ||
Expand Down Expand Up @@ -1229,6 +1171,15 @@ bool AdsImpl::ShowAd(
return false;
}

auto now_in_seconds = Time::NowInSeconds();

client_->AppendTimestampToCreativeSetHistoryForUuid(ad.creative_set_id,
now_in_seconds);
client_->AppendTimestampToCampaignHistoryForUuid(ad.campaign_id,
now_in_seconds);

client_->UpdateAdsUUIDSeen(ad.uuid, 1);

auto notification_info = std::make_unique<NotificationInfo>();
notification_info->id = base::GenerateGUID();
notification_info->advertiser = ad.advertiser;
Expand Down Expand Up @@ -1260,114 +1211,38 @@ bool AdsImpl::ShowAd(
}
#endif


client_->AppendCurrentTimeToCreativeSetHistory(ad.creative_set_id);
client_->AppendCurrentTimeToCampaignHistory(ad.campaign_id);

client_->UpdateAdsUUIDSeen(ad.uuid, 1);

return true;
}

bool AdsImpl::HistoryRespectsRollingTimeConstraint(
const std::deque<uint64_t> history,
const uint64_t seconds_window,
const uint64_t allowable_ad_count) const {
uint64_t recent_count = 0;

auto now_in_seconds = Time::NowInSeconds();

for (const auto& timestamp_in_seconds : history) {
if (now_in_seconds - timestamp_in_seconds < seconds_window) {
recent_count++;
}
}

if (recent_count <= allowable_ad_count) {
return true;
}

return false;
}

bool AdsImpl::HistoryRespectsRollingTimeConstraint(
const std::deque<AdHistoryDetail> history,
const uint64_t seconds_window,
const uint64_t allowable_ad_count) const {
uint64_t recent_count = 0;
std::vector<PermissionRule*> AdsImpl::CreatePermissionRules() const {
std::vector<PermissionRule*> permission_rules;

auto now_in_seconds = Time::NowInSeconds();
auto frequency_capping = std::make_unique<FrequencyCapping>(client_.get());

for (const auto& detail : history) {
if (now_in_seconds - detail.timestamp_in_seconds < seconds_window) {
recent_count++;
}
}
auto minimum_wait_time = std::make_unique<MinimumWaitTimeFrequencyCap>(this,
ads_client_, frequency_capping.get());
DCHECK(minimum_wait_time);
permission_rules.push_back(minimum_wait_time.get());

if (recent_count <= allowable_ad_count) {
return true;
}
auto per_day_limit = std::make_unique<PerDayLimitFrequencyCap>(
ads_client_, frequency_capping.get());
DCHECK(per_day_limit);
permission_rules.push_back(per_day_limit.get());

return false;
return permission_rules;
}

bool AdsImpl::IsAllowedToServeAds() {
auto does_history_respect_ads_per_day_limit =
DoesHistoryRespectAdsPerDayLimit();
auto permission_rules = CreatePermissionRules();

bool does_history_respect_minimum_wait_time;
if (!IsMobile()) {
does_history_respect_minimum_wait_time =
DoesHistoryRespectMinimumWaitTimeToServeAds();
} else {
does_history_respect_minimum_wait_time = true;
for (PermissionRule* permission_rule : permission_rules) {
if (!permission_rule->IsAllowed()) {
BLOG(INFO) << permission_rule->GetLastMessage();
return false;
}
}

BLOG(INFO) << "IsAllowedToServeAds:";
BLOG(INFO) << " does_history_respect_minimum_wait_time: "
<< does_history_respect_minimum_wait_time;
BLOG(INFO) << " does_history_respect_ads_per_day_limit: "
<< does_history_respect_ads_per_day_limit;

return does_history_respect_minimum_wait_time &&
does_history_respect_ads_per_day_limit;
}

bool AdsImpl::DoesHistoryRespectMinimumWaitTimeToServeAds() {
auto ads_shown_history = client_->GetAdsShownHistory();

auto hour_window = base::Time::kSecondsPerHour;
auto hour_allowed = ads_client_->GetAdsPerHour();
auto respects_hour_limit = HistoryRespectsRollingTimeConstraint(
ads_shown_history, hour_window, hour_allowed);

auto minimum_wait_time = hour_window / hour_allowed;
auto respects_minimum_wait_time = HistoryRespectsRollingTimeConstraint(
ads_shown_history, minimum_wait_time, 0);

BLOG(INFO) << "DoesHistoryRespectMinimumWaitTimeToServeAds:";
BLOG(INFO) << " respects_hour_limit: "
<< respects_hour_limit;
BLOG(INFO) << " respects_minimum_wait_time: "
<< respects_minimum_wait_time;

return respects_hour_limit && respects_minimum_wait_time;
}

bool AdsImpl::DoesHistoryRespectAdsPerDayLimit() {
auto ads_shown_history = client_->GetAdsShownHistory();

auto day_window = base::Time::kSecondsPerHour * base::Time::kHoursPerDay;
auto day_allowed = ads_client_->GetAdsPerDay();

auto respects_day_limit = HistoryRespectsRollingTimeConstraint(
ads_shown_history, day_window, day_allowed);

BLOG(INFO) << "DoesHistoryRespectAdsPerDayLimit:";
BLOG(INFO) << " respects_day_limit: "
<< respects_day_limit;

return respects_day_limit;
return true;
}

void AdsImpl::StartCollectingActivity(
Expand Down
Loading

0 comments on commit 9bdac46

Please sign in to comment.