#include "components/ukm/ukm_service.h"
#include <map>
#include <memory>
#include <set>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
#include "base/containers/contains.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/containers/to_vector.h"
#include "base/functional/bind.h"
#include "base/hash/hash.h"
#include "base/memory/raw_ptr.h"
#include "base/metrics/metrics_hashes.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/task/single_thread_task_runner.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/test/test_simple_task_runner.h"
#include "base/threading/platform_thread.h"
#include "base/time/time.h"
#include "components/metrics/cloned_install_detector.h"
#include "components/metrics/log_decoder.h"
#include "components/metrics/metrics_features.h"
#include "components/metrics/metrics_log.h"
#include "components/metrics/metrics_log_uploader.h"
#include "components/metrics/metrics_pref_names.h"
#include "components/metrics/test/test_metrics_provider.h"
#include "components/metrics/test/test_metrics_service_client.h"
#include "components/metrics/ukm_demographic_metrics_provider.h"
#include "components/metrics/unsent_log_store.h"
#include "components/prefs/testing_pref_service.h"
#include "components/ukm/observers/ukm_consent_state_observer.h"
#include "components/ukm/test_ukm_recorder.h"
#include "components/ukm/ukm_entry_filter.h"
#include "components/ukm/ukm_pref_names.h"
#include "components/ukm/ukm_recorder_impl.h"
#include "components/ukm/ukm_recorder_observer.h"
#include "components/ukm/unsent_log_store_metrics_impl.h"
#include "services/metrics/public/cpp/mojo_ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_builders.h"
#include "services/metrics/public/cpp/ukm_entry_builder.h"
#include "services/metrics/public/cpp/ukm_recorder.h"
#include "services/metrics/public/cpp/ukm_source.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/metrics/ukm_recorder_factory_impl.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/metrics_proto/ukm/report.pb.h"
#include "third_party/metrics_proto/ukm/source.pb.h"
#include "third_party/metrics_proto/ukm/web_features.pb.h"
#include "third_party/metrics_proto/user_demographics.pb.h"
namespace ukm {
TestEvent1;
const char* kTestEvent1Metric1 = …;
const char* kTestEvent1Metric2 = …;
TestEvent2;
const char* kTestEvent2Metric1 = …;
const char* kTestEvent2Metric2 = …;
TestEvent3;
TestProviderEvent;
const int32_t kWebDXFeature1 = …;
const int32_t kWebDXFeature3 = …;
const size_t kWebDXFeatureNumberOfFeaturesForTesting = …;
SourceId ConvertSourceIdToAllowlistedType(SourceId id, SourceIdType type) { … }
class TestRecordingHelper { … };
class TestMetricsServiceClientWithClonedInstallDetector
: public metrics::TestMetricsServiceClient { … };
namespace {
bool TestIsWebstoreExtension(std::string_view id) { … }
int GetPersistedLogCount(TestingPrefServiceSimple& prefs) { … }
Report GetPersistedReport(TestingPrefServiceSimple& prefs) { … }
metrics::LogMetadata GetPersistedLogMetadata(TestingPrefServiceSimple& prefs) { … }
void AddSourceToReport(Report& report,
int64_t other_id,
SourceIdType id_type,
std::string url) { … }
bool WebDXFeaturesStrictlyContains(const HighLevelWebFeatures& actual_features,
const std::set<int32_t>& expected_features) { … }
class ScopedUkmFeatureParams { … };
class MockDemographicMetricsProvider
: public metrics::UkmDemographicMetricsProvider { … };
class MockUkmRecorderObserver : public UkmRecorder::Observer { … };
class UkmTestMetricsProvider : public metrics::TestMetricsProvider { … };
class UkmServiceTest : public testing::Test { … };
class UkmReduceAddEntryIpcTest : public testing::Test { … };
}
TEST_F(UkmServiceTest, ClientIdMigration) { … }
TEST_F(UkmServiceTest, ClientIdClonedInstall) { … }
TEST_F(UkmServiceTest, EnableDisableSchedule) { … }
TEST_F(UkmServiceTest, PersistAndPurge) { … }
TEST_F(UkmServiceTest, Purge) { … }
TEST_F(UkmServiceTest, PurgeExtensionDataFromUnsentLogStore) { … }
TEST_F(UkmServiceTest, PurgeExtensionDataFromUnsentLogStoreWithVersionChange) { … }
TEST_F(UkmServiceTest, PurgeAppDataFromUnsentLogStore) { … }
TEST_F(UkmServiceTest, PurgeMsbbDataFromUnsentLogStore) { … }
TEST_F(UkmServiceTest, PurgeAppDataLogMetadataUpdate) { … }
TEST_F(UkmServiceTest, SourceSerialization) { … }
TEST_F(UkmServiceTest, SourceSerializationForAllowlistedButNonNavigationType) { … }
TEST_F(UkmServiceTest, LogMetadataOnlyAppKMSourceType) { … }
TEST_F(UkmServiceTest, LogMetadataOnlyUKMSourceType) { … }
TEST_F(UkmServiceTest, LogMetadataBothSourceType) { … }
TEST_F(UkmServiceTest, AddEntryWithEmptyMetrics) { … }
TEST_F(UkmServiceTest, MetricsProviderTest) { … }
TEST_F(UkmServiceTest, SystemProfileTest) { … }
TEST_F(UkmServiceTest, AddUserDemograhicsWhenAvailableAndFeatureEnabled) { … }
TEST_F(UkmServiceTest,
DontAddUserDemograhicsWhenNotAvailableAndFeatureEnabled) { … }
TEST_F(UkmServiceTest, DontAddUserDemograhicsWhenFeatureDisabled) { … }
TEST_F(UkmServiceTest, LogsRotation) { … }
TEST_F(UkmServiceTest, LogsUploadedOnlyWhenHavingData) { … }
TEST_F(UkmServiceTest, GetNewSourceID) { … }
TEST_F(UkmServiceTest, RecordRedirectedUrl) { … }
TEST_F(UkmServiceTest, RecordSessionId) { … }
TEST_F(UkmServiceTest, SourceSize) { … }
TEST_F(UkmServiceTest, PurgeMidUpload) { … }
TEST_F(UkmServiceTest, SourceURLLength) { … }
TEST_F(UkmServiceTest, UnreferencedNonAllowlistedSources) { … }
TEST_F(UkmServiceTest, NonAllowlistedUrls) { … }
TEST_F(UkmServiceTest, AllowlistIdType) { … }
TEST_F(UkmServiceTest, SupportedSchemes) { … }
TEST_F(UkmServiceTest, SupportedSchemesNoExtensions) { … }
TEST_F(UkmServiceTest, SanitizeUrlAuthParams) { … }
TEST_F(UkmServiceTest, SanitizeChromeUrlParams) { … }
TEST_F(UkmServiceTest, MarkSourceForDeletion) { … }
TEST_F(UkmServiceTest, PurgeNonCarriedOverSources) { … }
TEST_F(UkmServiceTest, IdentifiabilityMetricsDontExplode) { … }
TEST_F(UkmServiceTest, FilterCanRemoveMetrics) { … }
TEST_F(UkmServiceTest, FilterRejectsEvent) { … }
TEST_F(UkmServiceTest, PruneOldSources) { … }
TEST_F(UkmServiceTest, UseExternalClientID) { … }
TEST_F(UkmServiceTest, PurgeLogsOnClonedInstallDetected) { … }
TEST_F(UkmServiceTest, WebDXFeatures) { … }
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(UkmServiceTest, NotifyObserverOnShutdown) {
MockUkmRecorderObserver observer;
UkmService service(&prefs_, &client_,
std::make_unique<MockDemographicMetricsProvider>());
ukm::UkmRecorder::Get()->AddObserver(&observer);
EXPECT_CALL(observer, OnStartingShutdown()).Times(1);
}
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
namespace {
class UkmServiceTestWithIndependentAppKM
: public testing::TestWithParam<UkmConsentType> {
public:
UkmServiceTestWithIndependentAppKM()
: task_runner_(new base::TestSimpleTaskRunner),
task_runner_current_default_handle_(task_runner_) {
UkmService::RegisterPrefs(prefs_.registry());
prefs_.ClearPref(prefs::kUkmClientId);
prefs_.ClearPref(prefs::kUkmSessionId);
prefs_.ClearPref(prefs::kUkmUnsentLogStore);
}
int GetPersistedLogCount() { return ukm::GetPersistedLogCount(prefs_); }
Report GetPersistedReport() { return ukm::GetPersistedReport(prefs_); }
protected:
TestingPrefServiceSimple prefs_;
metrics::TestMetricsServiceClient client_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::SingleThreadTaskRunner::CurrentDefaultHandle
task_runner_current_default_handle_;
};
}
TEST_P(UkmServiceTestWithIndependentAppKM, RejectWhenNotConsented) {
const GURL kURL("https://google.com/foobar");
const GURL kAppURL("app://google.com/foobar");
const auto consent = GetParam();
const std::vector<int> app_indices = {1, 4};
const std::vector<int> url_indices = {0, 2, 3};
const std::vector<int>& expected_source_indices =
(consent == UkmConsentType::APPS ? app_indices : url_indices);
const int expected_result = expected_source_indices.size();
UkmService service(&prefs_, &client_,
std::make_unique<MockDemographicMetricsProvider>());
TestRecordingHelper recorder(&service);
EXPECT_EQ(0, GetPersistedLogCount());
service.Initialize();
task_runner_->RunUntilIdle();
service.UpdateRecording({consent});
service.EnableReporting();
std::vector<SourceId> source_ids;
for (int i = 0; i < 5; ++i) {
if (base::Contains(app_indices, i)) {
source_ids.push_back(UkmServiceTest::GetAppIDSourceId(i));
recorder.UpdateSourceURL(source_ids.back(), kAppURL);
} else {
source_ids.push_back(UkmServiceTest::GetAllowlistedSourceId(i));
recorder.UpdateSourceURL(source_ids.back(), kURL);
}
TestEvent1(source_ids.back()).Record(&service);
}
service.Flush(metrics::MetricsLogsEventManager::CreateReason::kUnknown);
EXPECT_EQ(1, GetPersistedLogCount());
Report report = GetPersistedReport();
EXPECT_EQ(expected_result, report.sources_size());
EXPECT_EQ(expected_result, report.entries_size());
for (int i = 0; i < expected_result; ++i) {
EXPECT_EQ(source_ids[expected_source_indices[i]], report.sources(i).id());
EXPECT_EQ(source_ids[expected_source_indices[i]],
report.entries(i).source_id());
}
}
INSTANTIATE_TEST_SUITE_P(
UkmServiceTestWithIndependentAppKMGroup,
UkmServiceTestWithIndependentAppKM,
testing::Values(UkmConsentType::APPS, UkmConsentType::MSBB),
[](const testing::TestParamInfo<
UkmServiceTestWithIndependentAppKM::ParamType>& info) {
if (info.param == UkmConsentType::APPS) {
return "TestApps";
} else {
return "TestMSBB";
}
});
namespace {
class UkmServiceTestWithIndependentAppKMFullConsent
: public testing::TestWithParam<bool> {
public:
UkmServiceTestWithIndependentAppKMFullConsent()
: task_runner_(new base::TestSimpleTaskRunner),
task_runner_current_default_handle_(task_runner_) {
UkmService::RegisterPrefs(prefs_.registry());
prefs_.ClearPref(prefs::kUkmClientId);
prefs_.ClearPref(prefs::kUkmSessionId);
prefs_.ClearPref(prefs::kUkmUnsentLogStore);
}
int GetPersistedLogCount() { return ukm::GetPersistedLogCount(prefs_); }
Report GetPersistedReport() { return ukm::GetPersistedReport(prefs_); }
protected:
TestingPrefServiceSimple prefs_;
metrics::TestMetricsServiceClient client_;
scoped_refptr<base::TestSimpleTaskRunner> task_runner_;
base::SingleThreadTaskRunner::CurrentDefaultHandle
task_runner_current_default_handle_;
};
}
TEST_P(UkmServiceTestWithIndependentAppKMFullConsent, VerifyAllAndNoneConsent) {
const GURL kURL("https://google.com/foobar");
const GURL kAppURL("app://google.com/foobar");
const int kExpectedResultWithConsent = 5;
const auto has_consent = GetParam();
const auto consent_state =
(has_consent ? UkmConsentState::All() : UkmConsentState());
UkmService service(&prefs_, &client_,
std::make_unique<MockDemographicMetricsProvider>());
TestRecordingHelper recorder(&service);
EXPECT_EQ(0, GetPersistedLogCount());
service.Initialize();
task_runner_->RunUntilIdle();
service.UpdateRecording(consent_state);
service.EnableReporting();
const std::vector<int> app_indices = {1, 4};
const std::vector<int> url_indices = {0, 2, 3};
std::vector<SourceId> source_ids;
for (int i = 0; i < 5; ++i) {
if (base::Contains(app_indices, i)) {
source_ids.push_back(UkmServiceTest::GetAppIDSourceId(i));
recorder.UpdateSourceURL(source_ids.back(), kAppURL);
} else {
source_ids.push_back(UkmServiceTest::GetAllowlistedSourceId(i));
recorder.UpdateSourceURL(source_ids.back(), kURL);
}
TestEvent1(source_ids.back()).Record(&service);
}
service.Flush(metrics::MetricsLogsEventManager::CreateReason::kUnknown);
EXPECT_EQ(GetPersistedLogCount(), static_cast<int>(has_consent));
if (has_consent) {
const auto report = GetPersistedReport();
EXPECT_EQ(report.sources_size(), kExpectedResultWithConsent);
EXPECT_EQ(report.entries_size(), kExpectedResultWithConsent);
for (int i = 0; i < kExpectedResultWithConsent; ++i) {
EXPECT_EQ(source_ids[i], report.sources(i).id());
EXPECT_EQ(source_ids[i], report.entries(i).source_id());
}
}
}
INSTANTIATE_TEST_SUITE_P(
UkmServiceTestWithIndependentAppKMFullConsentGroup,
UkmServiceTestWithIndependentAppKMFullConsent,
testing::Values(true, false),
[](const testing::TestParamInfo<
UkmServiceTestWithIndependentAppKMFullConsent::ParamType>& info) {
if (info.param) {
return "TestAllConsent";
} else {
return "TestNoConsent";
}
});
#endif
class MockUkmRecorder : public ukm::UkmRecorder { … };
TEST_F(UkmReduceAddEntryIpcTest, RecordingEnabled) { … }
TEST_F(UkmReduceAddEntryIpcTest, RecordingDisabled) { … }
TEST_F(UkmReduceAddEntryIpcTest, AddRemoveUkmObserver) { … }
TEST_F(UkmReduceAddEntryIpcTest, MultipleDelegates) { … }
}