From 8d7c0672f886ac7984cd1ff7d97c5e85d207db1a Mon Sep 17 00:00:00 2001 From: Malcolm Sparrow Date: Tue, 12 Nov 2019 16:35:36 +0000 Subject: [PATCH] Fixes Ads History popup shows 2 entries for the same ad (even if only served once) Fixes https://github.com/brave/brave-browser/issues/6205 --- components/services/bat_ads/bat_ads_impl.cc | 13 +- test/BUILD.gn | 1 + vendor/bat-native-ads/BUILD.gn | 3 + vendor/bat-native-ads/include/bat/ads/ads.h | 6 +- .../include/bat/ads/ads_history.h | 5 + .../src/bat/ads/internal/ads_impl.cc | 20 +- .../src/bat/ads/internal/ads_impl.h | 3 +- .../filters/ad_history_confirmation_filter.cc | 129 ++++++ .../filters/ad_history_confirmation_filter.h | 30 ++ ...ad_history_confirmation_filter_unittest.cc | 409 ++++++++++++++++++ .../ads/internal/filters/ad_history_filter.h | 25 ++ 11 files changed, 633 insertions(+), 11 deletions(-) create mode 100644 vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_confirmation_filter.cc create mode 100644 vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_confirmation_filter.h create mode 100644 vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_confirmation_filter_unittest.cc create mode 100644 vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_filter.h diff --git a/components/services/bat_ads/bat_ads_impl.cc b/components/services/bat_ads/bat_ads_impl.cc index c12ece4e6c9b..e3f8158cf616 100644 --- a/components/services/bat_ads/bat_ads_impl.cc +++ b/components/services/bat_ads/bat_ads_impl.cc @@ -159,13 +159,14 @@ void BatAdsImpl::GetAdsHistory( GetAdsHistoryCallback callback) { std::map> result; - auto ads_history_map = ads_->GetAdsHistory(); - for (const auto& entry : ads_history_map) { - std::vector ads_history; - for (const auto& ads_history_entry : entry.second) { - ads_history.push_back(ads_history_entry.ToJson()); + auto ads_histories = ads_->GetAdsHistory( + ads::AdsHistoryFilterType::kConfirmationType); + for (const auto& ads_history : ads_histories) { + std::vector ads_history_json; + for (const auto& ads_history_entry : ads_history.second) { + ads_history_json.push_back(ads_history_entry.ToJson()); } - result[entry.first] = ads_history; + result[ads_history.first] = ads_history_json; } std::move(callback).Run(mojo::MapToFlatMap(result)); diff --git a/test/BUILD.gn b/test/BUILD.gn index bf1344bfc72c..3b8ca185bbea 100644 --- a/test/BUILD.gn +++ b/test/BUILD.gn @@ -178,6 +178,7 @@ test("brave_unit_tests") { "//brave/components/brave_ads/browser/ads_service_impl_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/filters/ad_history_confirmation_filter_unittest.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", diff --git a/vendor/bat-native-ads/BUILD.gn b/vendor/bat-native-ads/BUILD.gn index 226f5f2ac794..b84e557c857c 100644 --- a/vendor/bat-native-ads/BUILD.gn +++ b/vendor/bat-native-ads/BUILD.gn @@ -152,6 +152,9 @@ 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/filters/ad_history_filter.h", + "src/bat/ads/internal/filters/ad_history_confirmation_filter.h", + "src/bat/ads/internal/filters/ad_history_confirmation_filter.cc", "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", diff --git a/vendor/bat-native-ads/include/bat/ads/ads.h b/vendor/bat-native-ads/include/bat/ads/ads.h index c03cd60295c7..58b44d7247e1 100644 --- a/vendor/bat-native-ads/include/bat/ads/ads.h +++ b/vendor/bat-native-ads/include/bat/ads/ads.h @@ -19,11 +19,10 @@ #include "bat/ads/notification_event_type.h" #include "bat/ads/notification_info.h" #include "bat/ads/public/interfaces/ads.mojom.h" +#include "bat/ads/ads_history.h" namespace ads { -struct AdsHistory; - using Environment = mojom::Environment; using InitializeCallback = std::function; @@ -206,7 +205,8 @@ class ADS_EXPORT Ads { // Should be called to get ads history. Returns |std::map>| in the format |{timestamp, array}| - virtual std::map> GetAdsHistory() = 0; + virtual std::map> GetAdsHistory( + const AdsHistoryFilterType ad_history_filterype) = 0; // Should be called to indicate interest in the specified ad. This is a // toggle, so calling it again returns the setting to the neutral state diff --git a/vendor/bat-native-ads/include/bat/ads/ads_history.h b/vendor/bat-native-ads/include/bat/ads/ads_history.h index d0abc0b6be86..99351bc00a83 100644 --- a/vendor/bat-native-ads/include/bat/ads/ads_history.h +++ b/vendor/bat-native-ads/include/bat/ads/ads_history.h @@ -16,6 +16,11 @@ namespace ads { struct AdHistoryDetail; +enum class AdsHistoryFilterType { + kNone = 0, + kConfirmationType +}; + struct ADS_EXPORT AdsHistory { AdsHistory(); explicit AdsHistory(const AdsHistory& history); diff --git a/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.cc b/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.cc index 00ea1449b3f6..2579d0083d64 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.cc +++ b/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.cc @@ -31,6 +31,7 @@ #include "bat/ads/internal/frequency_capping/permission_rules/minimum_wait_time_frequency_cap.h" #include "bat/ads/internal/frequency_capping/permission_rules/ads_per_day_frequency_cap.h" #include "bat/ads/internal/frequency_capping/permission_rules/ads_per_hour_frequency_cap.h" +#include "bat/ads/internal/filters/ad_history_confirmation_filter.h" #include "rapidjson/document.h" #include "rapidjson/error/en.h" @@ -550,11 +551,28 @@ void AdsImpl::SetConfirmationsIsReady( is_confirmations_ready_ = is_ready; } -std::map> AdsImpl::GetAdsHistory() { +std::map> AdsImpl::GetAdsHistory( + const AdsHistoryFilterType ads_history_filter_type) { std::map> ads_history; base::Time now = base::Time::Now().LocalMidnight(); auto ad_history_details = client_->GetAdsShownHistory(); + + std::unique_ptr ad_history_filter; + switch (ads_history_filter_type) { + case AdsHistoryFilterType::kNone: { + break; + } + case AdsHistoryFilterType::kConfirmationType: { + ad_history_filter = std::make_unique(); + break; + } + } + + if (ad_history_filter) { + ad_history_details = ad_history_filter->ApplyFilter(ad_history_details); + } + for (auto& detail_item : ad_history_details) { auto history_item = std::make_unique(); history_item->details.push_back(detail_item); diff --git a/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.h b/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.h index 9aeaec384e71..0233cb05c541 100644 --- a/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.h +++ b/vendor/bat-native-ads/src/bat/ads/internal/ads_impl.h @@ -127,7 +127,8 @@ class AdsImpl : public Ads { void SetConfirmationsIsReady( const bool is_ready) override; - std::map> GetAdsHistory() override; + std::map> GetAdsHistory( + const AdsHistoryFilterType ads_history_filter_type) override; AdContent::LikeAction ToggleAdThumbUp( const std::string& id, const std::string& creative_set_id, diff --git a/vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_confirmation_filter.cc b/vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_confirmation_filter.cc new file mode 100644 index 000000000000..a2d9d4bbf729 --- /dev/null +++ b/vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_confirmation_filter.cc @@ -0,0 +1,129 @@ +/* Copyright (c) 2019 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 +#include +#include + +#include "bat/ads/internal/filters/ad_history_confirmation_filter.h" +#include "bat/ads/confirmation_type.h" +#include "bat/ads/ad_history_detail.h" + +#include "bat/ads/ads_history.h" + +namespace ads { + +bool IsConfirmationTypeOfInterest( + const ConfirmationType& confirmation_type) { + bool is_of_interest = false; + + switch (confirmation_type.value()) { + case ConfirmationType::Value::CLICK: + case ConfirmationType::Value::VIEW: + case ConfirmationType::Value::DISMISS: { + is_of_interest = true; + break; + } + case ConfirmationType::Value::UNKNOWN: + case ConfirmationType::Value::LANDED: + case ConfirmationType::Value::FLAG: + case ConfirmationType::Value::UPVOTE: + case ConfirmationType::Value::DOWNVOTE: { + is_of_interest = false; + break; + } + } + return is_of_interest; +} + +bool DoesConfirmationTypeATrumpB( + const ConfirmationType& confirmation_type_a, + const ConfirmationType& confirmation_type_b) { + bool does_type_a_trump_type_b = false; + + switch (confirmation_type_a.value()) { + case ConfirmationType::Value::CLICK: { + switch (confirmation_type_b.value()) { + case ConfirmationType::Value::CLICK: + case ConfirmationType::Value::VIEW: + case ConfirmationType::Value::DISMISS: { + does_type_a_trump_type_b = true; + break; + } + default: { + break; + } + } + break; + } + case ConfirmationType::Value::VIEW: { + switch (confirmation_type_b.value()) { + case ConfirmationType::Value::VIEW: + case ConfirmationType::Value::DISMISS: { + does_type_a_trump_type_b = true; + break; + } + default: { + break; + } + } + break; + } + case ConfirmationType::Value::DISMISS: { + switch (confirmation_type_b.value()) { + case ConfirmationType::Value::DISMISS: { + does_type_a_trump_type_b = true; + break; + } + default: { + break; + } + } + break; + } + } + + return does_type_a_trump_type_b; +} + +AdHistoryConfirmationFilter::~AdHistoryConfirmationFilter() = default; + +std::deque AdHistoryConfirmationFilter::ApplyFilter( + const std::deque& ad_history_details) const { + + std::map filtered_ad_history; + + for (const AdHistoryDetail& ad_history_detail : ad_history_details) { + if (!IsConfirmationTypeOfInterest(ad_history_detail.ad_content.ad_action)) { + continue; + } + if (filtered_ad_history.count(ad_history_detail.ad_content.uuid) != 0) { + const AdHistoryDetail& check_ad_history_detail = + filtered_ad_history[ad_history_detail.ad_content.uuid]; + + if (ad_history_detail.timestamp_in_seconds >= + check_ad_history_detail.timestamp_in_seconds) { + if (DoesConfirmationTypeATrumpB(ad_history_detail.ad_content.ad_action, + check_ad_history_detail.ad_content.ad_action)) { + filtered_ad_history[ad_history_detail.ad_content.uuid] = + ad_history_detail; + } + } + } else { + filtered_ad_history[ad_history_detail.ad_content.uuid] = + ad_history_detail; + } + } + + std::deque filtered_ad_history_details; + + for (const auto& ad_history : filtered_ad_history) { + filtered_ad_history_details.push_back(ad_history.second); + } + + return filtered_ad_history_details; +} + +} // namespace ads diff --git a/vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_confirmation_filter.h b/vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_confirmation_filter.h new file mode 100644 index 000000000000..d65aaf96c9d5 --- /dev/null +++ b/vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_confirmation_filter.h @@ -0,0 +1,30 @@ +/* Copyright (c) 2019 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 BAT_ADS_INTERNAL_AD_HISTORY_CONFIRMATION_FILTER_H_ +#define BAT_ADS_INTERNAL_AD_HISTORY_CONFIRMATION_FILTER_H_ + +#include +#include +#include + +#include "bat/ads/internal/filters/ad_history_filter.h" + +namespace ads { + +struct AdsHistory; + +class AdHistoryConfirmationFilter : public AdHistoryFilter { + public : + AdHistoryConfirmationFilter() = default; + ~AdHistoryConfirmationFilter() override; + + std::deque ApplyFilter( + const std::deque& ad_history_details) const override; +}; + +} // namespace ads + +#endif // BAT_ADS_INTERNAL_AD_HISTORY_CONFIRMATION_FILTER_H_ diff --git a/vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_confirmation_filter_unittest.cc b/vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_confirmation_filter_unittest.cc new file mode 100644 index 000000000000..b9a023f6b537 --- /dev/null +++ b/vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_confirmation_filter_unittest.cc @@ -0,0 +1,409 @@ +/* Copyright (c) 2019 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 +#include + +#include "testing/gtest/include/gtest/gtest.h" + +#include "bat/ads/internal/filters/ad_history_confirmation_filter.h" + +#include "bat/ads/ads_history.h" +#include "bat/ads/ad_history_detail.h" +#include "bat/ads/internal/client_mock.h" +#include "bat/ads/internal/ads_client_mock.h" +#include "bat/ads/internal/ads_impl.h" +#include "bat/ads/internal/time.h" + +// npm run test -- brave_unit_tests --filter=Ads* + +using std::placeholders::_1; +using ::testing::_; +using ::testing::Invoke; + +namespace { + +const std::vector kTestAdUuids = { + "ab9deba5-01bf-492b-9bb8-7bc4318fe272", + "a577e7fe-d86c-4997-bbaa-4041dfd4075c", + "a6326b14-e4f4-4597-a358-ae6134eb26c1", +}; + +} // namespace + +namespace ads { + +class BraveAdsAdHistoryConfirmationFilterTest : public ::testing::Test { + protected: + BraveAdsAdHistoryConfirmationFilterTest() + : mock_ads_client_(std::make_unique()), + ads_(std::make_unique(mock_ads_client_.get())) { + // You can do set-up work for each test here + } + ~BraveAdsAdHistoryConfirmationFilterTest() override { + // You can do clean-up work that doesn't throw exceptions here + } + + // If the constructor and destructor are not enough for setting up and + // cleaning up each test, you can use the following methods + + void SetUp() override { + // Code here will be called immediately after the constructor (right before + // each test) + + auto callback = std::bind( + &BraveAdsAdHistoryConfirmationFilterTest::OnAdsImplInitialize, this, + _1); + ads_->Initialize(callback); + + client_mock_ = std::make_unique(ads_.get(), + mock_ads_client_.get()); + + ad_history_filter_ = std::make_unique(); + + ads_history_.clear(); + } + + void OnAdsImplInitialize(const Result result) { + EXPECT_EQ(Result::SUCCESS, result); + } + + void TearDown() override { + // Code here will be called immediately after each test (right before the + // destructor) + } + + void PopulateAdHistory(const std::string& ad_uuid, + const ConfirmationType::Value* values, const uint32_t items, + const uint64_t time_offset_per_item) { + AdHistoryDetail ad_history_detail; + + auto now_in_seconds = Time::NowInSeconds(); + + for (unsigned int i = 0; i < items; i++) { + ad_history_detail.ad_content.uuid = ad_uuid; + ad_history_detail.timestamp_in_seconds = now_in_seconds; + ad_history_detail.ad_content.ad_action = ConfirmationType(values[i]); + + ads_history_.push_back(ad_history_detail); + + now_in_seconds += time_offset_per_item; + } + } + + bool IsConfirmationTypeOfInterest(const ConfirmationType& confirmation_type) { + bool is_of_interest = false; + + if ((confirmation_type == ConfirmationType::Value::CLICK) + || (confirmation_type == ConfirmationType::Value::VIEW) + || (confirmation_type == ConfirmationType::Value::DISMISS)) { + is_of_interest = true; + } + + return is_of_interest; + } + + void TestFiltering(const std::string& ad_uuid, + ConfirmationType::Value expected_confirmation_type_value) { + std::map ad_history_detail_map; + + for (const AdHistoryDetail& adHistoryDetail : ads_history_filtered_) { + EXPECT_TRUE(IsConfirmationTypeOfInterest( + adHistoryDetail.ad_content.ad_action)); + + if (adHistoryDetail.ad_content.uuid == ad_uuid) { + ad_history_detail_map[adHistoryDetail.ad_content.uuid] = + adHistoryDetail; + } + } + + const AdHistoryDetail& ad_history_detail = ad_history_detail_map[ad_uuid]; + EXPECT_EQ(ad_history_detail.ad_content.ad_action.value(), + expected_confirmation_type_value); + } + + void TestFilteringWithTimestamps(const std::string& ad_uuid, + uint64_t expected_timestamp_in_seconds, + ConfirmationType::Value expected_confirmation_type_value) { + std::map ad_history_detail_map; + + for (const AdHistoryDetail& adHistoryDetail : ads_history_filtered_) { + EXPECT_TRUE(IsConfirmationTypeOfInterest( + adHistoryDetail.ad_content.ad_action)); + + if (adHistoryDetail.ad_content.uuid == ad_uuid) { + ad_history_detail_map[adHistoryDetail.ad_content.uuid] = + adHistoryDetail; + } + } + + const AdHistoryDetail& ad_history_detail = ad_history_detail_map[ad_uuid]; + EXPECT_EQ(ad_history_detail.timestamp_in_seconds, + expected_timestamp_in_seconds); + EXPECT_EQ(ad_history_detail.ad_content.ad_action.value(), + expected_confirmation_type_value); + } + + void PerformBasicUnitTest(const std::string& ad_uuid, + const ConfirmationType::Value* values, const uint32_t items, + const ConfirmationType::Value expected_confirmation_value) { + PopulateAdHistory(ad_uuid, values, items, 1); + + const AdHistoryDetail& expected_ad_history_detail = + ads_history_[1]; // Trump + const uint64_t expected_timestamp_in_seconds = + expected_ad_history_detail.timestamp_in_seconds; + + // Act + ads_history_filtered_ = ad_history_filter_->ApplyFilter(ads_history_); + + // Assert + TestFilteringWithTimestamps(ad_uuid, expected_timestamp_in_seconds, + expected_confirmation_value); + } + + std::unique_ptr mock_ads_client_; + std::unique_ptr ads_; + + std::unique_ptr client_mock_; + + std::deque ads_history_; + std::deque ads_history_filtered_; + std::unique_ptr ad_history_filter_; +}; + +TEST_F(BraveAdsAdHistoryConfirmationFilterTest, + NoFilteredResultsWhenNoAds) { + // Arrange + ConfirmationType::Value confirmation_types[] = { + }; + + size_t size_of_confirmation_types = sizeof(confirmation_types) / + sizeof(ConfirmationType::Value); + PopulateAdHistory(kTestAdUuids[0], confirmation_types, + size_of_confirmation_types, 1); + + // Act + ads_history_filtered_ = ad_history_filter_->ApplyFilter(ads_history_); + + // Assert + EXPECT_EQ(ads_history_filtered_.size(), (uint64_t)0); +} + +TEST_F(BraveAdsAdHistoryConfirmationFilterTest, + NoFilteredResultsForUnrecognisedConfirmationTypes) { + // Arrange + ConfirmationType::Value confirmation_types[] = { + ConfirmationType::Value::UNKNOWN, + ConfirmationType::Value::FLAG, + ConfirmationType::Value::UPVOTE, + ConfirmationType::Value::DOWNVOTE, + ConfirmationType::Value::LANDED, + }; + + size_t size_of_confirmation_types = sizeof(confirmation_types) / + sizeof(ConfirmationType::Value); + PopulateAdHistory(kTestAdUuids[0], confirmation_types, + size_of_confirmation_types, 1); + + // Act + ads_history_filtered_ = ad_history_filter_->ApplyFilter(ads_history_); + + // Assert + EXPECT_EQ(ads_history_.size(), (uint64_t)5); + EXPECT_EQ(ads_history_filtered_.size(), (uint64_t)0); +} + +TEST_F(BraveAdsAdHistoryConfirmationFilterTest, + FilteredDismissResultWithUnrecognisedConfirmationTypes) { + // Arrange + ConfirmationType::Value confirmation_types[] = { + ConfirmationType::Value::UNKNOWN, + ConfirmationType::Value::FLAG, + ConfirmationType::Value::DISMISS, // Trump + ConfirmationType::Value::UPVOTE, + ConfirmationType::Value::DOWNVOTE, + ConfirmationType::Value::LANDED, + }; + + size_t size_of_confirmation_types = sizeof(confirmation_types) / + sizeof(ConfirmationType::Value); + PopulateAdHistory(kTestAdUuids[0], confirmation_types, + size_of_confirmation_types, 1); + + const AdHistoryDetail& expected_ad_history_detail = + ads_history_[2]; // ::DISMISS + const uint64_t expected_timestamp = + expected_ad_history_detail.timestamp_in_seconds; + + // Act + ads_history_filtered_ = ad_history_filter_->ApplyFilter(ads_history_); + + // Assert + EXPECT_EQ(ads_history_.size(), (uint64_t)6); + EXPECT_EQ(ads_history_filtered_.size(), (uint64_t)1); + const AdHistoryDetail& ad_history_detail = ads_history_filtered_.front(); + EXPECT_EQ(ad_history_detail.timestamp_in_seconds, expected_timestamp); + EXPECT_EQ(ad_history_detail.ad_content.ad_action, + ConfirmationType::Value::DISMISS); +} + +TEST_F(BraveAdsAdHistoryConfirmationFilterTest, + ExpectLatestDismiss) { + // Arrange + ConfirmationType::Value confirmation_types[] = { + ConfirmationType::Value::DISMISS, + ConfirmationType::Value::DISMISS, // Trump + }; + + size_t size_of_confirmation_types = sizeof(confirmation_types) / + sizeof(ConfirmationType::Value); + PopulateAdHistory(kTestAdUuids[0], confirmation_types, + size_of_confirmation_types, 1); + + const AdHistoryDetail& expected_ad_history_detail = + ads_history_.back(); // Trump + const uint64_t expected_timestamp = + expected_ad_history_detail.timestamp_in_seconds; + + // Act + ads_history_filtered_ = ad_history_filter_->ApplyFilter(ads_history_); + + // Assert + EXPECT_EQ(ads_history_.size(), (uint64_t)2); + EXPECT_EQ(ads_history_filtered_.size(), (uint64_t)1); + const AdHistoryDetail& ad_history_detail = ads_history_filtered_.front(); + EXPECT_EQ(ad_history_detail.timestamp_in_seconds, expected_timestamp); + EXPECT_EQ(ad_history_detail.ad_content.ad_action, + ConfirmationType::Value::DISMISS); +} + +TEST_F(BraveAdsAdHistoryConfirmationFilterTest, + ViewTrumpsDismiss) { + // Arrange + const ConfirmationType::Value expected_confirmation_type = + ConfirmationType::Value::VIEW; + + ConfirmationType::Value confirmation_types[] = { + ConfirmationType::Value::DISMISS, + expected_confirmation_type, + ConfirmationType::Value::DISMISS, + }; + + size_t size_of_confirmation_types = sizeof(confirmation_types) / + sizeof(ConfirmationType::Value); + + PerformBasicUnitTest(kTestAdUuids[0], confirmation_types, + size_of_confirmation_types, expected_confirmation_type); +} + +TEST_F(BraveAdsAdHistoryConfirmationFilterTest, + ClickTrumpsDismiss) { + // Arrange + const ConfirmationType::Value expected_confirmation_type = + ConfirmationType::Value::CLICK; + + ConfirmationType::Value confirmation_types[] = { + ConfirmationType::Value::DISMISS, + expected_confirmation_type, + ConfirmationType::Value::DISMISS, + }; + + size_t size_of_confirmation_types = sizeof(confirmation_types) / + sizeof(ConfirmationType::Value); + + PerformBasicUnitTest(kTestAdUuids[0], confirmation_types, + size_of_confirmation_types, expected_confirmation_type); +} + +TEST_F(BraveAdsAdHistoryConfirmationFilterTest, + ClickTrumpsView) { + // Arrange + const ConfirmationType::Value expected_confirmation_type = + ConfirmationType::Value::CLICK; + + ConfirmationType::Value confirmation_types[] = { + ConfirmationType::Value::VIEW, + expected_confirmation_type, + ConfirmationType::Value::VIEW, + }; + + size_t size_of_confirmation_types = sizeof(confirmation_types) / + sizeof(ConfirmationType::Value); + + PerformBasicUnitTest(kTestAdUuids[0], confirmation_types, + size_of_confirmation_types, expected_confirmation_type); +} + +TEST_F(BraveAdsAdHistoryConfirmationFilterTest, + ClickTrumpsViewAndDismiss) { + // Arrange + const ConfirmationType::Value expected_confirmation_type = + ConfirmationType::Value::CLICK; + + ConfirmationType::Value confirmation_types[] = { + ConfirmationType::Value::DISMISS, + expected_confirmation_type, + ConfirmationType::Value::VIEW, + }; + + size_t size_of_confirmation_types = sizeof(confirmation_types) / + sizeof(ConfirmationType::Value); + + PerformBasicUnitTest(kTestAdUuids[0], confirmation_types, + size_of_confirmation_types, expected_confirmation_type); +} + +TEST_F(BraveAdsAdHistoryConfirmationFilterTest, + MultipleAdHistoriesFilterCorrectly) { + // Arrange + size_t size_of_confirmation_types = 0; + + ConfirmationType::Value confirmationTypesForAd1[] = { + ConfirmationType::Value::DISMISS, + ConfirmationType::Value::DISMISS, + ConfirmationType::Value::VIEW, // Trump + ConfirmationType::Value::DISMISS, + }; + + size_of_confirmation_types = sizeof(confirmationTypesForAd1) / + sizeof(ConfirmationType::Value); + PopulateAdHistory(kTestAdUuids[0], confirmationTypesForAd1, + size_of_confirmation_types, 1); + + ConfirmationType::Value confirmationTypesForAd2[] = { + ConfirmationType::Value::DISMISS, + ConfirmationType::Value::CLICK, // Trump + ConfirmationType::Value::VIEW, + ConfirmationType::Value::DISMISS, + }; + + size_of_confirmation_types = sizeof(confirmationTypesForAd2) / + sizeof(ConfirmationType::Value); + PopulateAdHistory(kTestAdUuids[1], confirmationTypesForAd2, + size_of_confirmation_types, 1); + + ConfirmationType::Value confirmationTypesForAd3[] = { + ConfirmationType::Value::CLICK, // Trump + ConfirmationType::Value::VIEW, + ConfirmationType::Value::DISMISS, + }; + + size_of_confirmation_types = sizeof(confirmationTypesForAd3) / + sizeof(ConfirmationType::Value); + PopulateAdHistory(kTestAdUuids[2], confirmationTypesForAd3, + size_of_confirmation_types, 1); + + // Act + ads_history_filtered_ = + ad_history_filter_->ApplyFilter(ads_history_); + + // Assert + TestFiltering(kTestAdUuids[0], ConfirmationType::Value::VIEW); + TestFiltering(kTestAdUuids[1], ConfirmationType::Value::CLICK); + TestFiltering(kTestAdUuids[2], ConfirmationType::Value::CLICK); +} + +} // namespace ads diff --git a/vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_filter.h b/vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_filter.h new file mode 100644 index 000000000000..113397b91c61 --- /dev/null +++ b/vendor/bat-native-ads/src/bat/ads/internal/filters/ad_history_filter.h @@ -0,0 +1,25 @@ +/* Copyright (c) 2019 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 BAT_ADS_INTERNAL_AD_HISTORY_FILTER_H_ +#define BAT_ADS_INTERNAL_AD_HISTORY_FILTER_H_ + +#include + +namespace ads { + +struct AdHistoryDetail; + +class AdHistoryFilter { + public: + virtual ~AdHistoryFilter() = default; + + virtual std::deque ApplyFilter( + const std::deque& ad_history_details) const = 0; +}; + +} // namespace ads + +#endif // BAT_ADS_INTERNAL_AD_HISTORY_FILTER_H_