// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chromeos/ash/components/report/report_controller.h"
#include "base/files/file_util.h"
#include "base/memory/ptr_util.h"
#include "base/no_destructor.h"
#include "base/path_service.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chromeos/ash/components/dbus/private_computing/private_computing_client.h"
#include "chromeos/ash/components/dbus/private_computing/private_computing_service.pb.h"
#include "chromeos/ash/components/dbus/session_manager/fake_session_manager_client.h"
#include "chromeos/ash/components/dbus/session_manager/session_manager_client.h"
#include "chromeos/ash/components/dbus/system_clock/system_clock_client.h"
#include "chromeos/ash/components/network/network_handler_test_helper.h"
#include "chromeos/ash/components/report/device_metrics/use_case/stub_psm_client_manager.h"
#include "chromeos/ash/components/report/device_metrics/use_case/use_case.h"
#include "chromeos/ash/components/report/prefs/fresnel_pref_names.h"
#include "chromeos/ash/components/report/utils/network_utils.h"
#include "chromeos/ash/components/report/utils/test_utils.h"
#include "chromeos/ash/components/system/fake_statistics_provider.h"
#include "components/prefs/testing_pref_service.h"
#include "components/version_info/channel.h"
#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/cros_system_api/dbus/shill/dbus-constants.h"
namespace psm_rlwe = private_membership::rlwe;
using pc_preserved_file_test =
private_computing::PrivateComputingClientRegressionTestData;
namespace ash::report::device_metrics {
class ReportControllerTestBase : public testing::Test {
public:
static private_computing::PrivateComputingClientRegressionTestData*
GetPreservedFileTestData() {
static base::NoDestructor<
private_computing::PrivateComputingClientRegressionTestData>
preserved_file_test_data;
return preserved_file_test_data.get();
}
static void CreatePreservedFileTestData() {
base::FilePath src_root_dir;
ASSERT_TRUE(
base::PathService::Get(base::DIR_SRC_TEST_DATA_ROOT, &src_root_dir));
const base::FilePath kPrivateComputingTestDataPath =
src_root_dir.AppendASCII("chromeos")
.AppendASCII("ash")
.AppendASCII("components")
.AppendASCII("report")
.AppendASCII("device_metrics")
.AppendASCII("testing")
.AppendASCII("preserved_file_test_data.binarypb");
ASSERT_TRUE(base::PathExists(kPrivateComputingTestDataPath));
ASSERT_TRUE(utils::ParseProtoFromFile(kPrivateComputingTestDataPath,
GetPreservedFileTestData()));
// Note that the test cases can change since it's read from the binary pb.
ASSERT_EQ(GetPreservedFileTestData()->test_cases_size(),
utils::kPreservedFileTestCaseSize);
}
static void SetUpTestSuite() {
// Initialize preserved file test data.
CreatePreservedFileTestData();
}
void SetUp() override {
// Set the mock clock to |kFakeTimeNow| or the configurable start ts.
base::Time ts = configurable_start_ts_;
if (ts.is_null()) {
// Default the mock time to |kFakeTimeNow|.
ASSERT_TRUE(
base::Time::FromUTCString(utils::kFakeTimeNowUnadjustedString, &ts));
}
ForwardClock(ts - base::Time::Now());
// Set up any necessary dependencies or objects before each test
PrivateComputingClient::InitializeFake();
SessionManagerClient::InitializeFake();
SystemClockClient::InitializeFake();
// Set a fake psm device active secret that is required to report use cases.
GetFakeSessionManagerClient()->set_psm_device_active_secret(
utils::kFakeHighEntropySeed);
// Set hardware class and activate date field, which are dependencies.
statistics_provider_.SetMachineStatistic(system::kHardwareClassKey,
"FAKE_HW_CLASS");
statistics_provider_.SetMachineStatistic(system::kActivateDateKey,
"2000-01");
system::StatisticsProvider::SetTestProvider(&statistics_provider_);
ReportController::RegisterPrefs(local_state_.registry());
test_shared_loader_factory_ =
base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
&test_url_loader_factory_);
// Network is not connected on device yet.
SetWifiNetworkState(shill::kStateNoConnectivity);
}
void TearDown() override {
// Shutdown fake clients in reverse order.
SystemClockClient::Shutdown();
SessionManagerClient::Shutdown();
PrivateComputingClient::Shutdown();
}
protected:
PrefService* GetLocalState() { return &local_state_; }
scoped_refptr<network::SharedURLLoaderFactory> GetUrlLoaderFactory() {
return test_shared_loader_factory_;
}
PrivateComputingClient::TestInterface* GetPrivateComputingTestInterface() {
return PrivateComputingClient::Get()->GetTestInterface();
}
FakeSessionManagerClient* GetFakeSessionManagerClient() {
return FakeSessionManagerClient::Get();
}
SystemClockClient::TestInterface* GetSystemClockTestInterface() {
return SystemClockClient::Get()->GetTestInterface();
}
void ResetLocalStateForTesting() {
const base::Time unix_epoch = base::Time::UnixEpoch();
GetLocalState()->SetTime(
prefs::kDeviceActiveLastKnown1DayActivePingTimestamp, unix_epoch);
GetLocalState()->SetTime(
prefs::kDeviceActiveLastKnown28DayActivePingTimestamp, unix_epoch);
GetLocalState()->SetTime(
prefs::kDeviceActiveChurnCohortMonthlyPingTimestamp, unix_epoch);
GetLocalState()->SetTime(
prefs::kDeviceActiveChurnObservationMonthlyPingTimestamp, unix_epoch);
GetLocalState()->SetInteger(prefs::kDeviceActiveLastKnownChurnActiveStatus,
0);
GetLocalState()->SetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus0, false);
GetLocalState()->SetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus1, false);
GetLocalState()->SetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus2, false);
}
void SetConfigurableStartTimestamp(base::Time start_ts) {
configurable_start_ts_ = start_ts;
}
void ForwardClock(base::TimeDelta delta) {
task_environment_.AdvanceClock(delta);
task_environment_.RunUntilIdle();
}
void SetWifiNetworkState(std::string network_state) {
std::stringstream ss;
ss << "{"
<< " \"GUID\": \""
<< "wifi_guid"
<< "\","
<< " \"Type\": \"" << shill::kTypeWifi << "\","
<< " \"State\": \"" << shill::kStateIdle << "\""
<< "}";
std::string wifi_network_service_path_ =
network_handler_test_helper_.ConfigureService(ss.str());
network_handler_test_helper_.SetServiceProperty(wifi_network_service_path_,
shill::kStateProperty,
base::Value(network_state));
task_environment_.RunUntilIdle();
}
// Generate a well-formed fake PSM network request and response bodies for
// testing purposes.
const std::string GetFresnelOprfResponse() {
FresnelPsmRlweOprfResponse psm_oprf_response;
*psm_oprf_response.mutable_rlwe_oprf_response() =
psm_rlwe::PrivateMembershipRlweOprfResponse();
return psm_oprf_response.SerializeAsString();
}
const std::string GetFresnelQueryResponse() {
FresnelPsmRlweQueryResponse psm_query_response;
*psm_query_response.mutable_rlwe_query_response() =
psm_rlwe::PrivateMembershipRlweQueryResponse();
return psm_query_response.SerializeAsString();
}
void SimulateOprfRequest(
StubPsmClientManagerDelegate* delegate,
const psm_rlwe::PrivateMembershipRlweOprfRequest& request) {
delegate->set_oprf_request(request);
}
void SimulateQueryRequest(
StubPsmClientManagerDelegate* delegate,
const psm_rlwe::PrivateMembershipRlweQueryRequest& request) {
delegate->set_query_request(request);
}
void SimulateMembershipResponses(
StubPsmClientManagerDelegate* delegate,
const private_membership::rlwe::RlweMembershipResponses&
membership_responses) {
delegate->set_membership_responses(membership_responses);
}
void SimulateOprfResponse(const std::string& serialized_response_body,
net::HttpStatusCode response_code) {
test_url_loader_factory_.SimulateResponseForPendingRequest(
utils::GetOprfRequestURL().spec(), serialized_response_body,
response_code);
task_environment_.RunUntilIdle();
}
// Generate a well-formed fake PSM network response body for testing purposes.
void SimulateQueryResponse(const std::string& serialized_response_body,
net::HttpStatusCode response_code) {
test_url_loader_factory_.SimulateResponseForPendingRequest(
utils::GetQueryRequestURL().spec(), serialized_response_body,
response_code);
task_environment_.RunUntilIdle();
}
void SimulateImportResponse(const std::string& serialized_response_body,
net::HttpStatusCode response_code) {
test_url_loader_factory_.SimulateResponseForPendingRequest(
utils::GetImportRequestURL().spec(), serialized_response_body,
response_code);
task_environment_.RunUntilIdle();
}
base::test::TaskEnvironment task_environment_{
base::test::TaskEnvironment::TimeSource::MOCK_TIME};
private:
// Derived test classes may pass a configurable start ts which is used to
// set the timestamp of the mock clock.
base::Time configurable_start_ts_;
scoped_refptr<network::SharedURLLoaderFactory> test_shared_loader_factory_;
network::TestURLLoaderFactory test_url_loader_factory_;
TestingPrefServiceSimple local_state_;
system::FakeStatisticsProvider statistics_provider_;
NetworkHandlerTestHelper network_handler_test_helper_;
};
class ReportControllerSimpleFlowTest : public ReportControllerTestBase {
public:
static constexpr ChromeDeviceMetadataParameters kFakeChromeParameters = {
version_info::Channel::STABLE /* chromeos_channel */,
MarketSegment::MARKET_SEGMENT_CONSUMER /* market_segment */,
};
void SetUp() override {
ReportControllerTestBase::SetUp();
// Default network to being synchronized and available.
GetSystemClockTestInterface()->SetServiceIsAvailable(true);
GetSystemClockTestInterface()->SetNetworkSynchronized(true);
// Default preserved file DBus operations to be empty.
GetPrivateComputingTestInterface()->SetGetLastPingDatesStatusResponse(
private_computing::GetStatusResponse());
GetPrivateComputingTestInterface()->SetSaveLastPingDatesStatusResponse(
private_computing::SaveStatusResponse());
// |psm_client_delegate| is owned by |psm_client_manager_|.
// Stub successful request payloads when created by the PSM client.
std::unique_ptr<StubPsmClientManagerDelegate> psm_client_delegate =
std::make_unique<StubPsmClientManagerDelegate>();
SimulateOprfRequest(psm_client_delegate.get(),
psm_rlwe::PrivateMembershipRlweOprfRequest());
SimulateQueryRequest(psm_client_delegate.get(),
psm_rlwe::PrivateMembershipRlweQueryRequest());
SimulateMembershipResponses(psm_client_delegate.get(),
GetMembershipResponses());
report_controller_ = std::make_unique<ReportController>(
kFakeChromeParameters, GetLocalState(), GetUrlLoaderFactory(),
std::make_unique<PsmClientManager>(std::move(psm_client_delegate)));
task_environment_.RunUntilIdle();
}
void TearDown() override {
report_controller_.reset();
// Shutdown dependency clients after |report_controller_| is destroyed.
ReportControllerTestBase::TearDown();
}
ReportController* GetReportController() { return report_controller_.get(); }
protected:
// Returns a single negative membership response.
psm_rlwe::RlweMembershipResponses GetMembershipResponses() {
psm_rlwe::RlweMembershipResponses membership_responses;
psm_rlwe::RlweMembershipResponses::MembershipResponseEntry* entry =
membership_responses.add_membership_responses();
private_membership::MembershipResponse* membership_response =
entry->mutable_membership_response();
membership_response->set_is_member(false);
return membership_responses;
}
private:
std::unique_ptr<ReportController> report_controller_;
};
TEST_F(ReportControllerSimpleFlowTest, ValidateSingletonObject) {
// The Get() method should return a valid singleton instance.
ReportController* instance = ReportController::Get();
EXPECT_NE(instance, nullptr);
EXPECT_EQ(instance, GetReportController());
}
TEST_F(ReportControllerSimpleFlowTest, CompleteFlowOnFreshDevice) {
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown1DayActivePingTimestamp),
base::Time::UnixEpoch());
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown28DayActivePingTimestamp),
base::Time::UnixEpoch());
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnCohortMonthlyPingTimestamp),
base::Time::UnixEpoch());
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnObservationMonthlyPingTimestamp),
base::Time::UnixEpoch());
EXPECT_EQ(
GetLocalState()->GetValue(prefs::kDeviceActiveLastKnownChurnActiveStatus),
0);
EXPECT_EQ(GetLocalState()->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus0),
false);
EXPECT_EQ(GetLocalState()->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus1),
false);
EXPECT_EQ(GetLocalState()->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus2),
false);
// Start reporting sequence.
SetWifiNetworkState(shill::kStateOnline);
// First mock network requests for 1DA use case.
SimulateOprfResponse(GetFresnelOprfResponse(), net::HTTP_OK);
SimulateQueryResponse(GetFresnelQueryResponse(), net::HTTP_OK);
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for 28DA use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Cohort use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Observation use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Ensure local state values are updated as expected.
base::Time pst_adjusted_ts;
ASSERT_TRUE(
base::Time::FromUTCString(utils::kFakeTimeNowString, &pst_adjusted_ts));
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown1DayActivePingTimestamp),
pst_adjusted_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown28DayActivePingTimestamp),
pst_adjusted_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnCohortMonthlyPingTimestamp),
pst_adjusted_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnObservationMonthlyPingTimestamp),
pst_adjusted_ts);
EXPECT_EQ(
GetLocalState()->GetValue(prefs::kDeviceActiveLastKnownChurnActiveStatus),
72351745);
EXPECT_EQ(GetLocalState()->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus0),
true);
EXPECT_EQ(GetLocalState()->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus1),
true);
EXPECT_EQ(GetLocalState()->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus2),
true);
}
TEST_F(ReportControllerSimpleFlowTest, DeviceFlowAcrossOneDay) {
// Start reporting sequence.
SetWifiNetworkState(shill::kStateOnline);
EXPECT_TRUE(GetReportController()->IsDeviceReportingForTesting());
// First mock network requests for 1DA use case.
SimulateOprfResponse(GetFresnelOprfResponse(), net::HTTP_OK);
SimulateQueryResponse(GetFresnelQueryResponse(), net::HTTP_OK);
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for 28DA use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Cohort use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Observation use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
EXPECT_FALSE(GetReportController()->IsDeviceReportingForTesting());
// Update mock time to be 1 day ahead.
base::TimeDelta day_delta = base::Days(1);
task_environment_.AdvanceClock(day_delta);
// Expected local state timestamp after updating clock 1 day ahead.
base::Time ts;
ASSERT_TRUE(base::Time::FromUTCString(utils::kFakeTimeNowString, &ts));
base::Time updated_ts = ts + day_delta;
// Trigger reporting use case sequence.
SetWifiNetworkState(shill::kStateNoConnectivity);
SetWifiNetworkState(shill::kStateOnline);
// 1DA will need to import in the new day.
SimulateImportResponse(std::string(), net::HTTP_OK);
// 28DA will need to import in the new day.
SimulateImportResponse(std::string(), net::HTTP_OK);
EXPECT_FALSE(GetReportController()->IsDeviceReportingForTesting());
// Ensure local state values are updated as expected.
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown1DayActivePingTimestamp),
updated_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown28DayActivePingTimestamp),
updated_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnCohortMonthlyPingTimestamp),
ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnObservationMonthlyPingTimestamp),
ts);
}
TEST_F(ReportControllerSimpleFlowTest, DeviceFlowAcrossOneWeek) {
// Start reporting sequence.
SetWifiNetworkState(shill::kStateOnline);
EXPECT_TRUE(GetReportController()->IsDeviceReportingForTesting());
// First mock network requests for 1DA use case.
SimulateOprfResponse(GetFresnelOprfResponse(), net::HTTP_OK);
SimulateQueryResponse(GetFresnelQueryResponse(), net::HTTP_OK);
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for 28DA use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Cohort use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Observation use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
EXPECT_FALSE(GetReportController()->IsDeviceReportingForTesting());
// Update mock time to be 7 days ahead.
base::TimeDelta week_delta = base::Days(7);
task_environment_.AdvanceClock(week_delta);
// Expected local state timestamp after updating clock 7 days ahead.
base::Time ts;
ASSERT_TRUE(base::Time::FromUTCString(utils::kFakeTimeNowString, &ts));
base::Time updated_ts = ts + week_delta;
// Trigger reporting use case sequence.
SetWifiNetworkState(shill::kStateNoConnectivity);
SetWifiNetworkState(shill::kStateOnline);
// 1DA will need to import in the new day.
SimulateImportResponse(std::string(), net::HTTP_OK);
// 28DA will need to import in the new day.
SimulateImportResponse(std::string(), net::HTTP_OK);
EXPECT_FALSE(GetReportController()->IsDeviceReportingForTesting());
// Ensure local state values are updated as expected.
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown1DayActivePingTimestamp),
updated_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown28DayActivePingTimestamp),
updated_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnCohortMonthlyPingTimestamp),
ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnObservationMonthlyPingTimestamp),
ts);
}
TEST_F(ReportControllerSimpleFlowTest, DeviceFlowAcrossOneMonth) {
// Start reporting sequence.
SetWifiNetworkState(shill::kStateOnline);
EXPECT_TRUE(GetReportController()->IsDeviceReportingForTesting());
// First mock network requests for 1DA use case.
SimulateOprfResponse(GetFresnelOprfResponse(), net::HTTP_OK);
SimulateQueryResponse(GetFresnelQueryResponse(), net::HTTP_OK);
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for 28DA use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Cohort use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Observation use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
EXPECT_FALSE(GetReportController()->IsDeviceReportingForTesting());
// Update mock time to be 1 month ahead.
base::TimeDelta month_delta = base::Days(31);
task_environment_.AdvanceClock(month_delta);
// Expected local state timestamp after updating clock.
base::Time ts;
ASSERT_TRUE(base::Time::FromUTCString(utils::kFakeTimeNowString, &ts));
base::Time updated_ts = ts + month_delta;
// Trigger reporting use case sequence.
SetWifiNetworkState(shill::kStateNoConnectivity);
SetWifiNetworkState(shill::kStateOnline);
// 1DA will need to import in the new day.
SimulateImportResponse(std::string(), net::HTTP_OK);
// 28DA will need to import in the new month.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Cohort will need to import in the new month.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Observation will need to import in the new month.
SimulateImportResponse(std::string(), net::HTTP_OK);
EXPECT_FALSE(GetReportController()->IsDeviceReportingForTesting());
// Ensure local state values are updated as expected.
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown1DayActivePingTimestamp),
updated_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown28DayActivePingTimestamp),
updated_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnCohortMonthlyPingTimestamp),
updated_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnObservationMonthlyPingTimestamp),
updated_ts);
EXPECT_EQ(
GetLocalState()->GetValue(prefs::kDeviceActiveLastKnownChurnActiveStatus),
72613891);
EXPECT_EQ(GetLocalState()->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus0),
true);
EXPECT_EQ(GetLocalState()->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus1),
true);
EXPECT_EQ(GetLocalState()->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus2),
true);
}
class ReportControllerPreservedFileReadWriteSuccessTest
: public ReportControllerTestBase {
public:
static constexpr ChromeDeviceMetadataParameters kFakeChromeParameters = {
version_info::Channel::STABLE /* chromeos_channel */,
MarketSegment::MARKET_SEGMENT_CONSUMER /* market_segment */,
};
void SetUp() override {
ReportControllerTestBase::SetUp();
// Default network to being synchronized and available.
GetSystemClockTestInterface()->SetServiceIsAvailable(true);
GetSystemClockTestInterface()->SetNetworkSynchronized(true);
// Default preserved file DBus operations to retrieve successfully.
pc_preserved_file_test::TestCase test = utils::GetPreservedFileTestCase(
GetPreservedFileTestData(),
pc_preserved_file_test::TestName::
PrivateComputingClientRegressionTestData_TestName_GET_SUCCESS_SAVE_SUCCESS);
GetPrivateComputingTestInterface()->SetGetLastPingDatesStatusResponse(
test.get_response());
GetPrivateComputingTestInterface()->SetSaveLastPingDatesStatusResponse(
test.save_response());
// |psm_client_delegate| is owned by |psm_client_manager_|.
// Stub successful request payloads when created by the PSM client.
std::unique_ptr<StubPsmClientManagerDelegate> psm_client_delegate =
std::make_unique<StubPsmClientManagerDelegate>();
SimulateOprfRequest(psm_client_delegate.get(),
psm_rlwe::PrivateMembershipRlweOprfRequest());
SimulateQueryRequest(psm_client_delegate.get(),
psm_rlwe::PrivateMembershipRlweQueryRequest());
SimulateMembershipResponses(psm_client_delegate.get(),
GetMembershipResponses());
report_controller_ = std::make_unique<ReportController>(
kFakeChromeParameters, GetLocalState(), GetUrlLoaderFactory(),
std::make_unique<PsmClientManager>(std::move(psm_client_delegate)));
task_environment_.RunUntilIdle();
}
void TearDown() override {
report_controller_.reset();
// Shutdown dependency clients after |report_controller_| is destroyed.
ReportControllerTestBase::TearDown();
}
protected:
ReportController* GetReportController() { return report_controller_.get(); }
// Returns a single negative membership response.
psm_rlwe::RlweMembershipResponses GetMembershipResponses() {
psm_rlwe::RlweMembershipResponses membership_responses;
psm_rlwe::RlweMembershipResponses::MembershipResponseEntry* entry =
membership_responses.add_membership_responses();
private_membership::MembershipResponse* membership_response =
entry->mutable_membership_response();
membership_response->set_is_member(false);
return membership_responses;
}
private:
std::unique_ptr<ReportController> report_controller_;
};
TEST_F(ReportControllerPreservedFileReadWriteSuccessTest, PreservedFileRead) {
// Local state prefs are updated by reading the preserved file.
base::Time pst_adjusted_ts;
ASSERT_TRUE(
base::Time::FromUTCString(utils::kFakeTimeNowString, &pst_adjusted_ts));
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown1DayActivePingTimestamp),
pst_adjusted_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown28DayActivePingTimestamp),
pst_adjusted_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnCohortMonthlyPingTimestamp),
pst_adjusted_ts);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnObservationMonthlyPingTimestamp),
pst_adjusted_ts);
EXPECT_EQ(
GetLocalState()->GetValue(prefs::kDeviceActiveLastKnownChurnActiveStatus),
72351745);
EXPECT_EQ(GetLocalState()->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus0),
true);
EXPECT_EQ(GetLocalState()->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus1),
true);
EXPECT_EQ(GetLocalState()->GetBoolean(
prefs::kDeviceActiveLastKnownIsActiveCurrentPeriodMinus2),
true);
}
class ReportControllerDeviceRecoveryTest : public ReportControllerTestBase {
public:
static constexpr ChromeDeviceMetadataParameters kFakeChromeParameters = {
version_info::Channel::STABLE /* chromeos_channel */,
MarketSegment::MARKET_SEGMENT_CONSUMER /* market_segment */,
};
void SetUp() override {
ReportControllerTestBase::SetUp();
// Default network to being synchronized and available.
GetSystemClockTestInterface()->SetServiceIsAvailable(true);
GetSystemClockTestInterface()->SetNetworkSynchronized(true);
// Default preserved file DBus operations to retrieve successfully.
pc_preserved_file_test::TestCase test = utils::GetPreservedFileTestCase(
GetPreservedFileTestData(),
pc_preserved_file_test::TestName::
PrivateComputingClientRegressionTestData_TestName_GET_SUCCESS_UNIX_EPOCH_PING_DATE_SAVE_SUCCESS);
GetPrivateComputingTestInterface()->SetGetLastPingDatesStatusResponse(
test.get_response());
GetPrivateComputingTestInterface()->SetSaveLastPingDatesStatusResponse(
test.save_response());
// |psm_client_delegate| is owned by |psm_client_manager_|.
// Stub successful request payloads when created by the PSM client.
std::unique_ptr<StubPsmClientManagerDelegate> psm_client_delegate =
std::make_unique<StubPsmClientManagerDelegate>();
SimulateOprfRequest(psm_client_delegate.get(),
psm_rlwe::PrivateMembershipRlweOprfRequest());
SimulateQueryRequest(psm_client_delegate.get(),
psm_rlwe::PrivateMembershipRlweQueryRequest());
SimulateMembershipResponses(psm_client_delegate.get(),
GetMembershipResponses());
report_controller_ = std::make_unique<ReportController>(
kFakeChromeParameters, GetLocalState(), GetUrlLoaderFactory(),
std::make_unique<PsmClientManager>(std::move(psm_client_delegate)));
task_environment_.RunUntilIdle();
}
void TearDown() override {
report_controller_.reset();
// Shutdown dependency clients after |report_controller_| is destroyed.
ReportControllerTestBase::TearDown();
}
protected:
ReportController* GetReportController() { return report_controller_.get(); }
// Returns a single negative membership response.
psm_rlwe::RlweMembershipResponses GetMembershipResponses() {
psm_rlwe::RlweMembershipResponses membership_responses;
psm_rlwe::RlweMembershipResponses::MembershipResponseEntry* entry =
membership_responses.add_membership_responses();
private_membership::MembershipResponse* membership_response =
entry->mutable_membership_response();
membership_response->set_is_member(false);
return membership_responses;
}
private:
std::unique_ptr<ReportController> report_controller_;
};
TEST_F(ReportControllerDeviceRecoveryTest,
ValidateCheckMembershipFlowOnRecovery) {
// Start reporting sequence.
SetWifiNetworkState(shill::kStateOnline);
EXPECT_TRUE(GetReportController()->IsDeviceReportingForTesting());
// First mock network requests for 1DA use case.
SimulateOprfResponse(GetFresnelOprfResponse(), net::HTTP_OK);
SimulateQueryResponse(GetFresnelQueryResponse(), net::HTTP_OK);
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for 28DA use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Cohort use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Observation use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
EXPECT_FALSE(GetReportController()->IsDeviceReportingForTesting());
// Reset local state so that when reporting flow begins again, the device will
// attempt check membership.
ResetLocalStateForTesting();
// Updating time 1 hour ahead will trigger timer to execute reporting flow.
ForwardClock(base::Minutes(60));
EXPECT_TRUE(GetReportController()->IsDeviceReportingForTesting());
// First mock network requests for 1DA use case.
SimulateOprfResponse(GetFresnelOprfResponse(), net::HTTP_OK);
SimulateQueryResponse(GetFresnelQueryResponse(), net::HTTP_OK);
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for 28DA use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Cohort use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Observation use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
EXPECT_FALSE(GetReportController()->IsDeviceReportingForTesting());
}
struct PingTestCase {
std::string test_name;
// String representation of the initial timestamp of the mock clock for this
// test.
std::string init_ts_as_str;
base::TimeDelta forward_clock_delta;
// Check if the expected use cases have sent another ping after forwarding the
// clock.
//
// Number of pending requests expected.
const int expected_pending_requests;
// Whether a daily ping is expected.
const bool expected_has_daily_pinged;
// Whether a 28-day ping is expected.
const bool expected_has_28da_pinged;
// Whether a cohort monthly ping is expected.
const bool expected_has_cohort_monthly_pinged;
// Whether an observation ping is expected.
const bool expected_has_observation_pinged;
};
class ReportControllerIsPingRequiredTest
: public ReportControllerTestBase,
public testing::WithParamInterface<PingTestCase> {
public:
static constexpr ChromeDeviceMetadataParameters kFakeChromeParameters = {
version_info::Channel::STABLE /* chromeos_channel */,
MarketSegment::MARKET_SEGMENT_CONSUMER /* market_segment */,
};
void SetUp() override {
ASSERT_TRUE(base::Time::FromUTCString(GetParam().init_ts_as_str.c_str(),
&init_ts_));
// Set the mock clock's initial start timestamp to match the
// value provided by the test case.
SetConfigurableStartTimestamp(init_ts_);
ReportControllerTestBase::SetUp();
// Verify that the mock clock was updated to |init_ts_| correctly.
ASSERT_EQ(base::Time::Now(), init_ts_);
// Default network to being synchronized and available.
GetSystemClockTestInterface()->SetServiceIsAvailable(true);
GetSystemClockTestInterface()->SetNetworkSynchronized(true);
// Default preserved file DBus operations to be empty.
GetPrivateComputingTestInterface()->SetGetLastPingDatesStatusResponse(
private_computing::GetStatusResponse());
GetPrivateComputingTestInterface()->SetSaveLastPingDatesStatusResponse(
private_computing::SaveStatusResponse());
// |psm_client_delegate| is owned by |psm_client_manager_|.
// Stub successful request payloads when created by the PSM client.
std::unique_ptr<StubPsmClientManagerDelegate> psm_client_delegate =
std::make_unique<StubPsmClientManagerDelegate>();
SimulateOprfRequest(psm_client_delegate.get(),
psm_rlwe::PrivateMembershipRlweOprfRequest());
SimulateQueryRequest(psm_client_delegate.get(),
psm_rlwe::PrivateMembershipRlweQueryRequest());
SimulateMembershipResponses(psm_client_delegate.get(),
GetMembershipResponses());
report_controller_ = std::make_unique<ReportController>(
kFakeChromeParameters, GetLocalState(), GetUrlLoaderFactory(),
std::make_unique<PsmClientManager>(std::move(psm_client_delegate)));
task_environment_.RunUntilIdle();
}
void TearDown() override {
report_controller_.reset();
// Shutdown dependency clients after |report_controller_| is destroyed.
ReportControllerTestBase::TearDown();
}
protected:
ReportController* GetReportController() { return report_controller_.get(); }
base::Time GetInitialTs() const { return init_ts_; }
// Returns a single negative membership response.
psm_rlwe::RlweMembershipResponses GetMembershipResponses() {
psm_rlwe::RlweMembershipResponses membership_responses;
psm_rlwe::RlweMembershipResponses::MembershipResponseEntry* entry =
membership_responses.add_membership_responses();
private_membership::MembershipResponse* membership_response =
entry->mutable_membership_response();
membership_response->set_is_member(false);
return membership_responses;
}
private:
// Initial timestamp that the mock clock is set to for current test instance.
base::Time init_ts_;
std::unique_ptr<ReportController> report_controller_;
};
TEST_P(ReportControllerIsPingRequiredTest, PingSuccessAcrossTimeBoundaries) {
EXPECT_TRUE(GetReportController()->IsDeviceReportingForTesting());
// First mock network requests for 1DA use case.
SimulateOprfResponse(GetFresnelOprfResponse(), net::HTTP_OK);
SimulateQueryResponse(GetFresnelQueryResponse(), net::HTTP_OK);
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for 28DA use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Cohort use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
// Next mock network requests for Observation use case.
SimulateImportResponse(std::string(), net::HTTP_OK);
task_environment_.RunUntilIdle();
// The stored ts in the local state prefs are adjusted to PST.
// Based on DST, the timestamps used in these tests are adjusted 8 hours.
base::Time initial_reporting_ts_adjusted = GetInitialTs() - base::Hours(8);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown1DayActivePingTimestamp),
initial_reporting_ts_adjusted);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown28DayActivePingTimestamp),
initial_reporting_ts_adjusted);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnCohortMonthlyPingTimestamp),
initial_reporting_ts_adjusted);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnObservationMonthlyPingTimestamp),
initial_reporting_ts_adjusted);
EXPECT_FALSE(GetReportController()->IsDeviceReportingForTesting());
// Adjust clock time.
ForwardClock(GetParam().forward_clock_delta);
EXPECT_TRUE(GetReportController()->IsDeviceReportingForTesting());
// Simulate the pending import requests that are required.
for (int i = 0; i < GetParam().expected_pending_requests; i++) {
SimulateImportResponse(std::string(), net::HTTP_OK);
}
base::Time expected_new_ping_ts =
initial_reporting_ts_adjusted + GetParam().forward_clock_delta;
// Verify that the expected use cases have sent another ping after
// forwarding the clock.
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown1DayActivePingTimestamp),
GetParam().expected_has_daily_pinged
? expected_new_ping_ts
: initial_reporting_ts_adjusted);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveLastKnown28DayActivePingTimestamp),
GetParam().expected_has_28da_pinged
? expected_new_ping_ts
: initial_reporting_ts_adjusted);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnCohortMonthlyPingTimestamp),
GetParam().expected_has_cohort_monthly_pinged
? expected_new_ping_ts
: initial_reporting_ts_adjusted);
EXPECT_EQ(GetLocalState()->GetTime(
prefs::kDeviceActiveChurnObservationMonthlyPingTimestamp),
GetParam().expected_has_observation_pinged
? expected_new_ping_ts
: initial_reporting_ts_adjusted);
}
INSTANTIATE_TEST_SUITE_P(
ReportControllerIsPingRequired,
ReportControllerIsPingRequiredTest,
testing::Values(
PingTestCase{.test_name = "EveningToMorningOverDayBoundaryTest",
.init_ts_as_str = "2023-01-02 20:00:00 GMT",
.forward_clock_delta = base::Hours(12),
.expected_pending_requests = 2,
.expected_has_daily_pinged = true,
.expected_has_28da_pinged = true,
.expected_has_cohort_monthly_pinged = false,
.expected_has_observation_pinged = false},
PingTestCase{.test_name = "MorningToEveningAcrossDayBoundaryTest",
.init_ts_as_str = "2023-01-02 08:00:00 GMT",
.forward_clock_delta = base::Hours(36),
.expected_pending_requests = 2,
.expected_has_daily_pinged = true,
.expected_has_28da_pinged = true,
.expected_has_cohort_monthly_pinged = false,
.expected_has_observation_pinged = false},
PingTestCase{.test_name = "EveningToMorningOverMonthBoundaryTest",
.init_ts_as_str = "2023-01-02 20:00:00 GMT",
.forward_clock_delta = base::Days(32) + base::Hours(12),
.expected_pending_requests = 4,
.expected_has_daily_pinged = true,
.expected_has_28da_pinged = true,
.expected_has_cohort_monthly_pinged = true,
.expected_has_observation_pinged = true},
PingTestCase{.test_name = "MorningToEveningAcrossMonthBoundaryTest",
.init_ts_as_str = "2022-01-02 08:00:00 GMT",
.forward_clock_delta = base::Days(32) + base::Hours(36),
.expected_pending_requests = 4,
.expected_has_daily_pinged = true,
.expected_has_28da_pinged = true,
.expected_has_cohort_monthly_pinged = true,
.expected_has_observation_pinged = true}));
} // namespace ash::report::device_metrics