blob: bcbc6c0174f2ca522ea4001b37ace0f777f59f70 [file] [log] [blame]
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/aggregation_service/aggregation_service_impl.h"
#include <memory>
#include <optional>
#include <utility>
#include <vector>
#include "base/files/scoped_temp_dir.h"
#include "base/functional/callback.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/metrics/histogram_base.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
#include "base/strings/string_util.h"
#include "base/test/bind.h"
#include "base/test/gmock_callback_support.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/time/time.h"
#include "content/browser/aggregation_service/aggregatable_report.h"
#include "content/browser/aggregation_service/aggregatable_report_assembler.h"
#include "content/browser/aggregation_service/aggregatable_report_scheduler.h"
#include "content/browser/aggregation_service/aggregatable_report_sender.h"
#include "content/browser/aggregation_service/aggregation_service_observer.h"
#include "content/browser/aggregation_service/aggregation_service_storage.h"
#include "content/browser/aggregation_service/aggregation_service_test_utils.h"
#include "content/public/test/browser_task_environment.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
namespace content {
namespace {
using ::content::aggregation_service::RequestIdIs;
using ::testing::_;
using ::testing::ElementsAre;
using ::testing::Eq;
using ::testing::Optional;
using ::testing::SizeIs;
using ::testing::StrictMock;
auto InvokeCallback(std::optional<AggregatableReport> report,
AggregationService::AssemblyStatus status) {
return [report = std::move(report), status = status](
AggregatableReportRequest report_request,
AggregationService::AssemblyCallback callback) {
std::move(callback).Run(std::move(report_request), std::move(report),
status);
};
}
AggregatableReport CreateExampleAggregatableReport() {
return AggregatableReport(AggregatableReport::AggregationServicePayload(
/*payload=*/kABCD1234AsBytes,
/*key_id=*/"key_1",
/*debug_cleartext_payload=*/std::nullopt),
"example_shared_info",
/*debug_key=*/std::nullopt,
/*additional_fields=*/{},
/*aggregation_coordinator_origin=*/std::nullopt);
}
AggregatableReportRequest CreateExampleRequestWithDelayType(
AggregatableReportRequest::DelayType delay_type) {
return aggregation_service::CreateExampleRequest(
/*failed_send_attempts=*/0,
/*aggregation_coordinator_origin=*/std::nullopt, delay_type);
}
} // namespace
class MockAggregatableReportAssembler : public AggregatableReportAssembler {
public:
explicit MockAggregatableReportAssembler(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: AggregatableReportAssembler(
/*storage_context=*/nullptr,
std::move(url_loader_factory)) {}
~MockAggregatableReportAssembler() override = default;
MOCK_METHOD(void,
AssembleReport,
(AggregatableReportRequest request, AssemblyCallback callback),
(override));
};
class MockAggregatableReportSender : public AggregatableReportSender {
public:
explicit MockAggregatableReportSender(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
: AggregatableReportSender(std::move(url_loader_factory)) {}
~MockAggregatableReportSender() override = default;
MOCK_METHOD(void,
SendReport,
(GURL url,
const base::Value& contents,
std::optional<AggregatableReportRequest::DelayType> delay_type,
ReportSentCallback callback),
(override));
};
class MockAggregatableReportScheduler : public AggregatableReportScheduler {
public:
explicit MockAggregatableReportScheduler(
AggregationServiceStorageContext* storage_context)
: AggregatableReportScheduler(storage_context, base::DoNothing()) {}
~MockAggregatableReportScheduler() override = default;
MOCK_METHOD(void,
ScheduleRequest,
(AggregatableReportRequest request),
(override));
MOCK_METHOD(void,
NotifyInProgressRequestSucceeded,
(AggregationServiceStorage::RequestId request_id),
(override));
MOCK_METHOD(bool,
NotifyInProgressRequestFailed,
(AggregationServiceStorage::RequestId request_id,
int previous_failed_attempts),
(override));
};
class MockAggregationServiceObserver : public AggregationServiceObserver {
public:
MockAggregationServiceObserver() = default;
~MockAggregationServiceObserver() override = default;
MOCK_METHOD(void, OnRequestStorageModified, (), (override));
MOCK_METHOD(void,
OnReportHandled,
(const AggregatableReportRequest& request,
std::optional<AggregationServiceStorage::RequestId> id,
const std::optional<AggregatableReport>& report,
base::Time report_handle_time,
ReportStatus status),
(override));
};
class AggregationServiceImplTest : public testing::Test {
public:
AggregationServiceImplTest()
: task_environment_(base::test::TaskEnvironment::TimeSource::MOCK_TIME),
storage_context_(task_environment_.GetMockClock()) {
EXPECT_TRUE(dir_.CreateUniqueTempDir());
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
auto assembler =
std::make_unique<MockAggregatableReportAssembler>(url_loader_factory);
test_assembler_ = assembler.get();
auto sender =
std::make_unique<MockAggregatableReportSender>(url_loader_factory);
test_sender_ = sender.get();
auto scheduler =
std::make_unique<MockAggregatableReportScheduler>(&storage_context_);
test_scheduler_ = scheduler.get();
service_impl_ = AggregationServiceImpl::CreateForTesting(
/*run_in_memory=*/true, dir_.GetPath(),
task_environment_.GetMockClock(), std::move(scheduler),
std::move(assembler), std::move(sender));
}
void StoreReport(AggregatableReportRequest request) {
service_impl_->storage_.AsyncCall(&AggregationServiceStorage::StoreRequest)
.WithArgs(std::move(request));
}
void OnScheduledReportTimeReached(
std::vector<AggregationServiceStorage::RequestAndId> requests) {
service_impl_->OnScheduledReportTimeReached(std::move(requests));
}
// Verify bucket counts and/or total counts of relevant histograms nested
// under "PrivacySandbox.AggregationService.ScheduledRequests".
//
// Args:
// `statuses`: The complete expected contents of the Status histogram.
// `num_retries_recorded`: The expected number of samples in the
// NumRetriesBeforeSuccess histogram. (If you expect
// one send with zero retries, that's one sample!)
// `num_assemblies`: The number of samples expected for the AssemblyTime
// histogram.
// `num_sends`: The number of samples expected for the SendAttemptTime
// histogram.
void VerifyScheduledHistograms(
std::vector<base::Bucket> statuses = {},
base::HistogramBase::Count32 num_successful_sends = 0,
base::HistogramBase::Count32 num_assemblies = 0,
base::HistogramBase::Count32 num_attempted_sends = 0,
bool scheduled_with_full_delay = true) {
EXPECT_THAT(
histogram_tester_.GetAllSamples(
"PrivacySandbox.AggregationService.ScheduledRequests.Status"),
base::BucketsAreArray(statuses));
EXPECT_THAT(histogram_tester_.GetAllSamples(base::JoinString(
{"PrivacySandbox.AggregationService",
scheduled_with_full_delay ? "ScheduledWithFullDelay"
: "ScheduledWithReducedDelay",
"FinalRequestStatus"},
".")),
base::BucketsAreArray(statuses));
histogram_tester_.ExpectTotalCount(
"PrivacySandbox.AggregationService.ScheduledRequests."
"NumRetriesBeforeSuccess",
num_successful_sends);
histogram_tester_.ExpectTotalCount(
base::JoinString(
{"PrivacySandbox.AggregationService",
scheduled_with_full_delay ? "ScheduledWithFullDelay"
: "ScheduledWithReducedDelay",
"NumRetriesBeforeSuccess"},
"."),
num_successful_sends);
histogram_tester_.ExpectTotalCount(
"PrivacySandbox.AggregationService.ScheduledRequests.AssemblyTime",
num_assemblies);
histogram_tester_.ExpectTotalCount(
"PrivacySandbox.AggregationService.ScheduledRequests.SendAttemptTime",
num_attempted_sends);
histogram_tester_.ExpectTotalCount(
"PrivacySandbox.AggregationService.ScheduledRequests."
"DelayFromOriginalReportTime",
num_attempted_sends);
}
// Verify bucket counts and/or total counts of relevant histograms nested
// under "PrivacySandbox.AggregationService.UnscheduledRequests".
//
// Args:
// `statuses`: The complete expected contents of the Status histogram.
// `num_assemblies`: The number of samples expected for the AssemblyTime
// histogram.
// `num_sends`: The number of samples expected for the SendAttemptTime
// histogram.
void VerifyUnscheduledHistograms(
std::vector<base::Bucket> statuses = {},
base::HistogramBase::Count32 num_assemblies = 0,
base::HistogramBase::Count32 num_attempted_sends = 0) {
EXPECT_THAT(
histogram_tester_.GetAllSamples(
"PrivacySandbox.AggregationService.UnscheduledRequests.Status"),
base::BucketsAreArray(statuses));
EXPECT_THAT(
histogram_tester_.GetAllSamples(
"PrivacySandbox.AggregationService.Unscheduled.FinalRequestStatus"),
base::BucketsAreArray(statuses));
histogram_tester_.ExpectTotalCount(
"PrivacySandbox.AggregationService.UnscheduledRequests.AssemblyTime",
num_assemblies);
histogram_tester_.ExpectTotalCount(
"PrivacySandbox.AggregationService.UnscheduledRequests.SendAttemptTime",
num_attempted_sends);
histogram_tester_.ExpectTotalCount(
"PrivacySandbox.AggregationService.UnscheduledRequests."
"DelayFromOriginalReportTime",
num_attempted_sends);
}
protected:
base::ScopedTempDir dir_;
BrowserTaskEnvironment task_environment_;
network::TestURLLoaderFactory test_url_loader_factory_;
TestAggregationServiceStorageContext storage_context_;
std::unique_ptr<AggregationServiceImpl> service_impl_;
raw_ptr<MockAggregatableReportAssembler> test_assembler_ = nullptr;
raw_ptr<MockAggregatableReportSender> test_sender_ = nullptr;
raw_ptr<MockAggregatableReportScheduler> test_scheduler_ = nullptr;
base::HistogramTester histogram_tester_;
};
TEST_F(AggregationServiceImplTest, AssembleReport_Succeed) {
EXPECT_CALL(*test_assembler_, AssembleReport)
.WillOnce(
InvokeCallback(CreateExampleAggregatableReport(),
AggregatableReportAssembler::AssemblyStatus::kOk));
base::RunLoop run_loop;
service_impl_->AssembleReport(
aggregation_service::CreateExampleRequest(),
base::BindLambdaForTesting(
[&](AggregatableReportRequest request,
std::optional<AggregatableReport> report,
AggregationService::AssemblyStatus status) {
EXPECT_TRUE(report.has_value());
EXPECT_EQ(status, AggregationService::AssemblyStatus::kOk);
run_loop.Quit();
}));
run_loop.Run();
VerifyScheduledHistograms();
VerifyUnscheduledHistograms();
}
TEST_F(AggregationServiceImplTest, AssembleReport_Fail) {
EXPECT_CALL(*test_assembler_, AssembleReport)
.WillOnce(InvokeCallback(
/*report=*/std::nullopt,
AggregatableReportAssembler::AssemblyStatus::kPublicKeyFetchFailed));
base::RunLoop run_loop;
service_impl_->AssembleReport(
aggregation_service::CreateExampleRequest(),
base::BindLambdaForTesting(
[&](AggregatableReportRequest request,
std::optional<AggregatableReport> report,
AggregationService::AssemblyStatus status) {
EXPECT_FALSE(report.has_value());
EXPECT_EQ(
status,
AggregationService::AssemblyStatus::kPublicKeyFetchFailed);
run_loop.Quit();
}));
run_loop.Run();
VerifyScheduledHistograms();
VerifyUnscheduledHistograms();
}
TEST_F(AggregationServiceImplTest, ScheduleReport_Success) {
std::vector<AggregationServiceStorage::RequestAndId> requests_and_ids;
// Request IDs begin at 1.
AggregationServiceStorage::RequestId request_id(1);
EXPECT_CALL(*test_scheduler_, ScheduleRequest)
.WillOnce([&](AggregatableReportRequest request) {
requests_and_ids.push_back(AggregationServiceStorage::RequestAndId{
.request = std::move(request),
.id = request_id,
});
});
service_impl_->ScheduleReport(aggregation_service::CreateExampleRequest());
EXPECT_CALL(*test_assembler_, AssembleReport)
.WillOnce(
InvokeCallback(CreateExampleAggregatableReport(),
AggregatableReportAssembler::AssemblyStatus::kOk));
EXPECT_CALL(*test_sender_, SendReport)
.WillOnce(base::test::RunOnceCallback<3>(
AggregatableReportSender::RequestStatus::kOk));
EXPECT_CALL(*test_scheduler_, NotifyInProgressRequestSucceeded(request_id));
EXPECT_THAT(requests_and_ids, SizeIs(1));
OnScheduledReportTimeReached(std::move(requests_and_ids));
VerifyScheduledHistograms(
/*statuses=*/{{AggregationServiceObserver::ReportStatus::kSent, 1}},
/*num_successful_sends=*/1,
/*num_assemblies=*/1,
/*num_attempted_sends=*/1);
VerifyUnscheduledHistograms();
histogram_tester_.ExpectUniqueSample(
"PrivacySandbox.AggregationService.ScheduledRequests."
"NumRetriesBeforeSuccess",
/*sample=*/0, 1);
histogram_tester_.ExpectUniqueSample(
"PrivacySandbox.AggregationService.ScheduledWithFullDelay."
"NumRetriesBeforeSuccess",
/*sample=*/0, 1);
}
TEST_F(AggregationServiceImplTest, ScheduleReport_Success_ReducedDelay) {
std::vector<AggregationServiceStorage::RequestAndId> requests_and_ids;
// Request IDs begin at 1.
AggregationServiceStorage::RequestId request_id(1);
EXPECT_CALL(*test_scheduler_, ScheduleRequest)
.WillOnce([&](AggregatableReportRequest request) {
requests_and_ids.push_back(AggregationServiceStorage::RequestAndId{
.request = std::move(request),
.id = request_id,
});
});
service_impl_->ScheduleReport(CreateExampleRequestWithDelayType(
AggregatableReportRequest::DelayType::ScheduledWithReducedDelay));
EXPECT_CALL(*test_assembler_, AssembleReport)
.WillOnce(
InvokeCallback(CreateExampleAggregatableReport(),
AggregatableReportAssembler::AssemblyStatus::kOk));
EXPECT_CALL(*test_sender_, SendReport)
.WillOnce(base::test::RunOnceCallback<3>(
AggregatableReportSender::RequestStatus::kOk));
EXPECT_CALL(*test_scheduler_, NotifyInProgressRequestSucceeded(request_id));
EXPECT_THAT(requests_and_ids, SizeIs(1));
OnScheduledReportTimeReached(std::move(requests_and_ids));
VerifyScheduledHistograms(
/*statuses=*/{{AggregationServiceObserver::ReportStatus::kSent, 1}},
/*num_successful_sends=*/1,
/*num_assemblies=*/1,
/*num_attempted_sends=*/1,
/*scheduled_with_full_delay=*/false);
VerifyUnscheduledHistograms();
histogram_tester_.ExpectUniqueSample(
"PrivacySandbox.AggregationService.ScheduledRequests."
"NumRetriesBeforeSuccess",
/*sample=*/0, 1);
histogram_tester_.ExpectUniqueSample(
"PrivacySandbox.AggregationService.ScheduledWithReducedDelay."
"NumRetriesBeforeSuccess",
/*sample=*/0, 1);
}
TEST_F(AggregationServiceImplTest, ScheduleReport_FailedAssembly) {
std::vector<AggregationServiceStorage::RequestAndId> requests_and_ids;
// Request IDs begin at 1.
AggregationServiceStorage::RequestId request_id(1);
EXPECT_CALL(*test_scheduler_, ScheduleRequest)
.WillOnce([&](AggregatableReportRequest request) {
requests_and_ids.push_back(AggregationServiceStorage::RequestAndId{
.request = std::move(request),
.id = request_id,
});
});
AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
/*failed_send_attempts=*/AggregatableReportScheduler::kMaxRetries);
service_impl_->ScheduleReport(std::move(request));
EXPECT_CALL(*test_assembler_, AssembleReport)
.WillOnce(InvokeCallback(
/*report=*/std::nullopt,
AggregatableReportAssembler::AssemblyStatus::kAssemblyFailed));
EXPECT_CALL(*test_scheduler_,
NotifyInProgressRequestFailed(
request_id, AggregatableReportScheduler::kMaxRetries))
.WillOnce(::testing::Return(false));
StrictMock<MockAggregationServiceObserver> observer;
base::ScopedObservation<AggregationService, AggregationServiceObserver>
observation(&observer);
observation.Observe(service_impl_.get());
EXPECT_CALL(observer, OnRequestStorageModified);
EXPECT_CALL(observer,
OnReportHandled(
_, Optional(request_id), _, _,
AggregationServiceObserver::ReportStatus::kFailedToAssemble));
EXPECT_THAT(requests_and_ids, SizeIs(1));
OnScheduledReportTimeReached(std::move(requests_and_ids));
VerifyScheduledHistograms(
/*statuses=*/{{AggregationServiceObserver::ReportStatus::
kFailedToAssemble,
1}},
/*num_successful_sends=*/0,
/*num_assemblies=*/1,
/*num_attempted_sends=*/0);
VerifyUnscheduledHistograms();
}
TEST_F(AggregationServiceImplTest, ScheduleReport_Failure_ReducedDelay) {
std::vector<AggregationServiceStorage::RequestAndId> requests_and_ids;
// Request IDs begin at 1.
AggregationServiceStorage::RequestId request_id(1);
EXPECT_CALL(*test_scheduler_, ScheduleRequest)
.WillOnce([&](AggregatableReportRequest request) {
requests_and_ids.push_back(AggregationServiceStorage::RequestAndId{
.request = std::move(request),
.id = request_id,
});
});
AggregatableReportRequest request = aggregation_service::CreateExampleRequest(
/*failed_send_attempts=*/AggregatableReportScheduler::kMaxRetries,
/*aggregation_coordinator_origin=*/std::nullopt, /*delay_type=*/
AggregatableReportRequest::DelayType::ScheduledWithReducedDelay);
service_impl_->ScheduleReport(std::move(request));
EXPECT_CALL(*test_assembler_, AssembleReport)
.WillOnce(InvokeCallback(
/*report=*/std::nullopt,
AggregatableReportAssembler::AssemblyStatus::kAssemblyFailed));
EXPECT_CALL(*test_scheduler_,
NotifyInProgressRequestFailed(
request_id, AggregatableReportScheduler::kMaxRetries))
.WillOnce(::testing::Return(false));
StrictMock<MockAggregationServiceObserver> observer;
base::ScopedObservation<AggregationService, AggregationServiceObserver>
observation(&observer);
observation.Observe(service_impl_.get());
EXPECT_CALL(observer, OnRequestStorageModified);
EXPECT_CALL(observer,
OnReportHandled(
_, Optional(request_id), _, _,
AggregationServiceObserver::ReportStatus::kFailedToAssemble));
EXPECT_THAT(requests_and_ids, SizeIs(1));
OnScheduledReportTimeReached(std::move(requests_and_ids));
VerifyScheduledHistograms(
/*statuses=*/{{AggregationServiceObserver::ReportStatus::
kFailedToAssemble,
1}},
/*num_successful_sends=*/0,
/*num_assemblies=*/1,
/*num_attempted_sends=*/0,
/*scheduled_with_full_delay=*/false);
VerifyUnscheduledHistograms();
}
TEST_F(AggregationServiceImplTest, ScheduleReport_FailedSending) {
std::vector<AggregationServiceStorage::RequestAndId> requests_and_ids;
// Request IDs begin at 1.
AggregationServiceStorage::RequestId request_id(1);
EXPECT_CALL(*test_scheduler_, ScheduleRequest)
.WillOnce([&](AggregatableReportRequest request) {
requests_and_ids.push_back(AggregationServiceStorage::RequestAndId{
.request = std::move(request),
.id = request_id,
});
});
service_impl_->ScheduleReport(aggregation_service::CreateExampleRequest());
EXPECT_CALL(*test_assembler_, AssembleReport)
.WillOnce(
InvokeCallback(CreateExampleAggregatableReport(),
AggregatableReportAssembler::AssemblyStatus::kOk));
EXPECT_CALL(*test_sender_, SendReport)
.WillOnce(base::test::RunOnceCallback<3>(
AggregatableReportSender::RequestStatus::kNetworkError));
EXPECT_CALL(*test_scheduler_, NotifyInProgressRequestFailed(
request_id, /*previous_failed_attempts=*/0))
.WillOnce(::testing::Return(true));
StrictMock<MockAggregationServiceObserver> observer;
base::ScopedObservation<AggregationService, AggregationServiceObserver>
observation(&observer);
observation.Observe(service_impl_.get());
EXPECT_CALL(observer, OnRequestStorageModified);
// The report should not be considered handled when it is scheduled for a
// retry
EXPECT_CALL(observer, OnReportHandled).Times(0);
EXPECT_THAT(requests_and_ids, SizeIs(1));
OnScheduledReportTimeReached(std::move(requests_and_ids));
VerifyScheduledHistograms(
/*statuses=*/{},
/*num_successful_sends=*/0,
/*num_assemblies=*/1,
/*num_attempted_sends=*/1);
VerifyUnscheduledHistograms();
}
TEST_F(AggregationServiceImplTest,
MultipleReportsReturnedFromScheduler_Success) {
std::vector<AggregationServiceStorage::RequestAndId> requests_and_ids;
// Request IDs begin at 1.
AggregationServiceStorage::RequestId request_id_1(1);
AggregationServiceStorage::RequestId request_id_2(2);
EXPECT_CALL(*test_scheduler_, ScheduleRequest)
.WillOnce([&](AggregatableReportRequest request) {
requests_and_ids.push_back(AggregationServiceStorage::RequestAndId{
.request = std::move(request),
.id = request_id_1,
});
})
.WillOnce([&](AggregatableReportRequest request) {
requests_and_ids.push_back(AggregationServiceStorage::RequestAndId{
.request = std::move(request),
.id = request_id_2,
});
});
AggregatableReportRequest request_1 =
aggregation_service::CreateExampleRequest();
AggregatableReportRequest request_2 =
aggregation_service::CreateExampleRequest(
/*failed_send_attempts=*/2);
service_impl_->ScheduleReport(std::move(request_1));
service_impl_->ScheduleReport(std::move(request_2));
EXPECT_CALL(*test_assembler_, AssembleReport)
.WillRepeatedly(
InvokeCallback(CreateExampleAggregatableReport(),
AggregatableReportAssembler::AssemblyStatus::kOk));
EXPECT_CALL(*test_sender_, SendReport)
.WillRepeatedly(base::test::RunOnceCallbackRepeatedly<3>(
AggregatableReportSender::RequestStatus::kOk));
EXPECT_CALL(*test_scheduler_, NotifyInProgressRequestSucceeded(request_id_1));
EXPECT_CALL(*test_scheduler_, NotifyInProgressRequestSucceeded(request_id_2));
EXPECT_THAT(requests_and_ids, SizeIs(2));
OnScheduledReportTimeReached(std::move(requests_and_ids));
VerifyScheduledHistograms(
/*statuses=*/{{AggregationServiceObserver::ReportStatus::kSent, 2}},
/*num_successful_sends=*/2,
/*num_assemblies=*/2,
/*num_attempted_sends=*/2);
VerifyUnscheduledHistograms();
histogram_tester_.ExpectBucketCount(
"PrivacySandbox.AggregationService.ScheduledRequests."
"NumRetriesBeforeSuccess",
/*sample=*/0, 1);
histogram_tester_.ExpectBucketCount(
"PrivacySandbox.AggregationService.ScheduledRequests."
"NumRetriesBeforeSuccess",
/*sample=*/2, 1);
}
TEST_F(AggregationServiceImplTest, AssembleAndSendReport_Success) {
EXPECT_CALL(*test_assembler_, AssembleReport)
.WillOnce(
InvokeCallback(CreateExampleAggregatableReport(),
AggregatableReportAssembler::AssemblyStatus::kOk));
EXPECT_CALL(*test_sender_, SendReport)
.WillOnce(base::test::RunOnceCallback<3>(
AggregatableReportSender::RequestStatus::kOk));
StrictMock<MockAggregationServiceObserver> observer;
base::ScopedObservation<AggregationService, AggregationServiceObserver>
observation(&observer);
observation.Observe(service_impl_.get());
EXPECT_CALL(observer,
OnReportHandled(_, Eq(std::nullopt), _, _,
AggregationServiceObserver::ReportStatus::kSent));
// The scheduler should not have been interacted with.
EXPECT_CALL(*test_scheduler_, NotifyInProgressRequestSucceeded).Times(0);
EXPECT_CALL(*test_scheduler_, NotifyInProgressRequestFailed).Times(0);
service_impl_->AssembleAndSendReport(CreateExampleRequestWithDelayType(
AggregatableReportRequest::DelayType::Unscheduled));
VerifyScheduledHistograms();
VerifyUnscheduledHistograms(
/*statuses=*/
{{AggregationServiceObserver::ReportStatus::kSent, 1}},
/*num_assemblies=*/1,
/*num_attempted_sends=*/1);
}
TEST_F(AggregationServiceImplTest, AssembleAndSendReport_FailedAssembly) {
EXPECT_CALL(*test_assembler_, AssembleReport)
.WillOnce(InvokeCallback(
/*report=*/std::nullopt,
AggregatableReportAssembler::AssemblyStatus::kAssemblyFailed));
StrictMock<MockAggregationServiceObserver> observer;
base::ScopedObservation<AggregationService, AggregationServiceObserver>
observation(&observer);
observation.Observe(service_impl_.get());
EXPECT_CALL(observer,
OnReportHandled(
_, Eq(std::nullopt), _, _,
AggregationServiceObserver::ReportStatus::kFailedToAssemble));
// The scheduler should not have been interacted with.
EXPECT_CALL(*test_scheduler_, NotifyInProgressRequestSucceeded).Times(0);
EXPECT_CALL(*test_scheduler_, NotifyInProgressRequestFailed).Times(0);
service_impl_->AssembleAndSendReport(CreateExampleRequestWithDelayType(
AggregatableReportRequest::DelayType::Unscheduled));
VerifyScheduledHistograms();
VerifyUnscheduledHistograms(
/*statuses=*/{{AggregationServiceObserver::ReportStatus::
kFailedToAssemble,
1}},
/*num_assemblies=*/1);
}
TEST_F(AggregationServiceImplTest, AssembleAndSendReport_FailedSender) {
EXPECT_CALL(*test_assembler_, AssembleReport)
.WillOnce(
InvokeCallback(CreateExampleAggregatableReport(),
AggregatableReportAssembler::AssemblyStatus::kOk));
EXPECT_CALL(*test_sender_, SendReport)
.WillOnce(base::test::RunOnceCallback<3>(
AggregatableReportSender::RequestStatus::kNetworkError));
StrictMock<MockAggregationServiceObserver> observer;
base::ScopedObservation<AggregationService, AggregationServiceObserver>
observation(&observer);
observation.Observe(service_impl_.get());
EXPECT_CALL(
observer,
OnReportHandled(_, Eq(std::nullopt), _, _,
AggregationServiceObserver::ReportStatus::kFailedToSend));
// The scheduler should not have been interacted with.
EXPECT_CALL(*test_scheduler_, NotifyInProgressRequestSucceeded).Times(0);
EXPECT_CALL(*test_scheduler_, NotifyInProgressRequestFailed).Times(0);
service_impl_->AssembleAndSendReport(CreateExampleRequestWithDelayType(
AggregatableReportRequest::DelayType::Unscheduled));
VerifyScheduledHistograms();
VerifyUnscheduledHistograms(
/*statuses=*/
{{AggregationServiceObserver::ReportStatus::kFailedToSend, 1}},
/*num_assemblies=*/1,
/*num_attempted_sends=*/1);
}
TEST_F(AggregationServiceImplTest, GetPendingReportRequestsForWebUI) {
StoreReport(CreateExampleRequestWithDelayType(
AggregatableReportRequest::DelayType::ScheduledWithFullDelay));
StoreReport(CreateExampleRequestWithDelayType(
AggregatableReportRequest::DelayType::ScheduledWithFullDelay));
base::RunLoop run_loop;
service_impl_->GetPendingReportRequestsForWebUI(base::BindLambdaForTesting(
[&](std::vector<AggregationServiceStorage::RequestAndId>
requests_and_ids) {
// IDs autoincrement from 1.
EXPECT_THAT(
requests_and_ids,
ElementsAre(RequestIdIs(AggregationServiceStorage::RequestId(1)),
RequestIdIs(AggregationServiceStorage::RequestId(2))));
run_loop.Quit();
}));
run_loop.Run();
}
TEST_F(AggregationServiceImplTest, SendReportsForWebUI) {
StoreReport(CreateExampleRequestWithDelayType(
AggregatableReportRequest::DelayType::ScheduledWithFullDelay));
EXPECT_CALL(*test_assembler_, AssembleReport)
.WillOnce(
InvokeCallback(CreateExampleAggregatableReport(),
AggregatableReportAssembler::AssemblyStatus::kOk));
base::RunLoop run_loop;
EXPECT_CALL(*test_sender_, SendReport)
.WillOnce(
testing::DoAll(base::test::RunOnceClosure(run_loop.QuitClosure()),
base::test::RunOnceCallback<3>(
AggregatableReportSender::RequestStatus::kOk)));
// IDs autoincrement from 1.
AggregationServiceStorage::RequestId request_id(1);
StrictMock<MockAggregationServiceObserver> observer;
base::ScopedObservation<AggregationService, AggregationServiceObserver>
observation(&observer);
observation.Observe(service_impl_.get());
EXPECT_CALL(observer, OnRequestStorageModified);
EXPECT_CALL(observer, OnReportHandled(_, Optional(request_id), _, _,
AggregationServiceObserver::kSent));
service_impl_->SendReportsForWebUI({request_id}, base::DoNothing());
run_loop.Run();
}
TEST_F(AggregationServiceImplTest, ClearData_NotifyObservers) {
StrictMock<MockAggregationServiceObserver> observer;
base::ScopedObservation<AggregationService, AggregationServiceObserver>
observation(&observer);
observation.Observe(service_impl_.get());
EXPECT_CALL(observer, OnRequestStorageModified);
base::RunLoop run_loop;
service_impl_->ClearData(/*delete_begin=*/base::Time::Min(),
/*delete_end=*/base::Time::Max(),
/*filter=*/base::NullCallback(),
run_loop.QuitClosure());
run_loop.Run();
}
} // namespace content