1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "components/metrics/structured/structured_metrics_service.h"
6
7 #include <memory>
8
9 #include "base/files/file_path.h"
10 #include "base/files/file_util.h"
11 #include "base/files/scoped_temp_dir.h"
12 #include "base/test/scoped_feature_list.h"
13 #include "base/test/task_environment.h"
14 #include "base/test/test_simple_task_runner.h"
15 #include "components/metrics/log_decoder.h"
16 #include "components/metrics/metrics_provider.h"
17 #include "components/metrics/structured/recorder.h"
18 #include "components/metrics/structured/reporting/structured_metrics_reporting_service.h"
19 #include "components/metrics/structured/structured_events.h"
20 #include "components/metrics/structured/structured_metrics_features.h"
21 #include "components/metrics/structured/structured_metrics_prefs.h"
22 #include "components/metrics/structured/structured_metrics_recorder.h"
23 #include "components/metrics/structured/test/test_event_storage.h"
24 #include "components/metrics/structured/test/test_key_data_provider.h"
25 #include "components/metrics/test/test_metrics_service_client.h"
26 #include "components/metrics/unsent_log_store.h"
27 #include "components/metrics/unsent_log_store_metrics_impl.h"
28 #include "components/prefs/testing_pref_service.h"
29 #include "testing/gmock/include/gmock/gmock.h"
30 #include "testing/gtest/include/gtest/gtest.h"
31
32 namespace metrics::structured {
33 namespace {
34
35 using events::v2::test_project_one::TestEventOne;
36 using events::v2::test_project_six::TestEventSeven;
37
38 // The name hash of "TestProjectOne".
39 constexpr uint64_t kProjectOneHash = UINT64_C(16881314472396226433);
40 // The name hash of "TestProjectThree".
41 constexpr uint64_t kProjectThreeHash = UINT64_C(10860358748803291132);
42
43 class TestRecorder : public StructuredMetricsClient::RecordingDelegate {
44 public:
45 TestRecorder() = default;
46 TestRecorder(const TestRecorder& recorder) = delete;
47 TestRecorder& operator=(const TestRecorder& recorder) = delete;
48 ~TestRecorder() override = default;
49
RecordEvent(Event && event)50 void RecordEvent(Event&& event) override {
51 Recorder::GetInstance()->RecordEvent(std::move(event));
52 }
53
IsReadyToRecord() const54 bool IsReadyToRecord() const override { return true; }
55 };
56
57 } // namespace
58
59 class StructuredMetricsServiceTest : public testing::Test {
60 public:
StructuredMetricsServiceTest()61 StructuredMetricsServiceTest() {
62 reporting::StructuredMetricsReportingService::RegisterPrefs(
63 prefs_.registry());
64 }
65
SetUp()66 void SetUp() override {
67 feature_list_.InitWithFeatures({kEnabledStructuredMetricsService}, {});
68
69 Recorder::GetInstance()->SetUiTaskRunner(
70 task_environment_.GetMainThreadTaskRunner());
71 StructuredMetricsClient::Get()->SetDelegate(&test_recorder_);
72
73 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
74
75 WriteTestingDeviceKeys();
76
77 WriteTestingProfileKeys();
78 }
79
TearDown()80 void TearDown() override { StructuredMetricsClient::Get()->UnsetDelegate(); }
81
Init()82 void Init() {
83 auto recorder = std::make_unique<StructuredMetricsRecorder>(
84 std::make_unique<TestKeyDataProvider>(DeviceKeyFilePath(),
85 ProfileKeyFilePath()),
86 std::make_unique<TestEventStorage>());
87
88 recorder->OnProfileAdded(temp_dir_.GetPath());
89 service_ = std::make_unique<StructuredMetricsService>(&client_, &prefs_,
90 std::move(recorder));
91 Wait();
92 }
93
EnableRecording()94 void EnableRecording() { service_->EnableRecording(); }
EnableReporting()95 void EnableReporting() { service_->EnableReporting(); }
96
DisableRecording()97 void DisableRecording() { service_->DisableRecording(); }
DisableReporting()98 void DisableReporting() { service_->DisableReporting(); }
99
ProfileKeyFilePath()100 base::FilePath ProfileKeyFilePath() {
101 return temp_dir_.GetPath()
102 .Append(FILE_PATH_LITERAL("structured_metrics"))
103 .Append(FILE_PATH_LITERAL("keys"));
104 }
105
DeviceKeyFilePath()106 base::FilePath DeviceKeyFilePath() {
107 return temp_dir_.GetPath()
108 .Append(FILE_PATH_LITERAL("structured_metrics"))
109 .Append(FILE_PATH_LITERAL("device_keys"));
110 }
111
DeviceEventsFilePath()112 base::FilePath DeviceEventsFilePath() {
113 return temp_dir_.GetPath()
114 .Append(FILE_PATH_LITERAL("structured_metrics"))
115 .Append(FILE_PATH_LITERAL("events"));
116 }
117
WriteTestingProfileKeys()118 void WriteTestingProfileKeys() {
119 const int today = (base::Time::Now() - base::Time::UnixEpoch()).InDays();
120
121 KeyDataProto proto;
122 KeyProto& key_one = (*proto.mutable_keys())[kProjectOneHash];
123 key_one.set_key("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
124 key_one.set_last_rotation(today);
125 key_one.set_rotation_period(90);
126
127 KeyProto& key_three = (*proto.mutable_keys())[kProjectThreeHash];
128 key_three.set_key("cccccccccccccccccccccccccccccccc");
129 key_three.set_last_rotation(today);
130 key_three.set_rotation_period(90);
131
132 base::CreateDirectory(ProfileKeyFilePath().DirName());
133 ASSERT_TRUE(
134 base::WriteFile(ProfileKeyFilePath(), proto.SerializeAsString()));
135 Wait();
136 }
137
WriteTestingDeviceKeys()138 void WriteTestingDeviceKeys() {
139 base::CreateDirectory(DeviceKeyFilePath().DirName());
140 ASSERT_TRUE(base::WriteFile(DeviceKeyFilePath(),
141 KeyDataProto().SerializeAsString()));
142 Wait();
143 }
144
GetPersistedLogCount()145 int GetPersistedLogCount() {
146 return prefs_.GetList(prefs::kLogStoreName).size();
147 }
148
GetPersistedLog()149 ChromeUserMetricsExtension GetPersistedLog() {
150 EXPECT_THAT(GetPersistedLogCount(), 1);
151 metrics::UnsentLogStore result_unsent_log_store(
152 std::make_unique<UnsentLogStoreMetricsImpl>(), &prefs_,
153 prefs::kLogStoreName, /*metadata_pref_name=*/nullptr,
154 // Set to 3 so logs are not dropped in the test.
155 UnsentLogStore::UnsentLogStoreLimits{
156 .min_log_count = 3,
157 },
158 /*signing_key=*/std::string(),
159 /*logs_event_manager=*/nullptr);
160
161 result_unsent_log_store.LoadPersistedUnsentLogs();
162 result_unsent_log_store.StageNextLog();
163
164 ChromeUserMetricsExtension uma_proto;
165 EXPECT_TRUE(metrics::DecodeLogDataToProto(
166 result_unsent_log_store.staged_log(), &uma_proto));
167 return uma_proto;
168 }
169
service()170 StructuredMetricsService& service() { return *service_.get(); }
171
Wait()172 void Wait() { task_environment_.RunUntilIdle(); }
173
AdvanceClock(int hours)174 void AdvanceClock(int hours) {
175 task_environment_.AdvanceClock(base::Hours(hours));
176 }
177
178 protected:
179 std::unique_ptr<StructuredMetricsService> service_;
180 metrics::TestMetricsServiceClient client_;
181
182 private:
183 base::test::ScopedFeatureList feature_list_;
184 TestingPrefServiceSimple prefs_;
185
186 TestRecorder test_recorder_;
187 base::ScopedTempDir temp_dir_;
188
189 base::test::TaskEnvironment task_environment_{
190 base::test::TaskEnvironment::MainThreadType::UI,
191 base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED,
192 base::test::TaskEnvironment::TimeSource::MOCK_TIME};
193 };
194
TEST_F(StructuredMetricsServiceTest,PurgeInMemory)195 TEST_F(StructuredMetricsServiceTest, PurgeInMemory) {
196 Init();
197
198 EnableRecording();
199 EnableReporting();
200
201 TestEventOne().SetTestMetricTwo(1).Record();
202 TestEventSeven().SetTestMetricSeven(1.0).Record();
203
204 service_->Purge();
205 service_->Flush(metrics::MetricsLogsEventManager::CreateReason::kUnknown);
206
207 // Nothing should be stored.
208 EXPECT_THAT(GetPersistedLogCount(), 0);
209 }
210
TEST_F(StructuredMetricsServiceTest,PurgePersisted)211 TEST_F(StructuredMetricsServiceTest, PurgePersisted) {
212 Init();
213
214 EnableRecording();
215 EnableReporting();
216
217 TestEventOne().SetTestMetricTwo(1).Record();
218 TestEventSeven().SetTestMetricSeven(1.0).Record();
219
220 service_->Flush(metrics::MetricsLogsEventManager::CreateReason::kUnknown);
221
222 service_->Purge();
223
224 // Need to make sure there is a log to read.
225 service_->Flush(metrics::MetricsLogsEventManager::CreateReason::kUnknown);
226
227 // Nothing should be stored.
228 EXPECT_THAT(GetPersistedLogCount(), 0);
229 }
230
TEST_F(StructuredMetricsServiceTest,RotateLogs)231 TEST_F(StructuredMetricsServiceTest, RotateLogs) {
232 Init();
233
234 EnableRecording();
235 EnableReporting();
236
237 TestEventOne().SetTestMetricTwo(1).Record();
238 TestEventSeven().SetTestMetricSeven(1).Record();
239
240 service_->Flush(metrics::MetricsLogsEventManager::CreateReason::kUnknown);
241
242 const auto uma_proto = GetPersistedLog();
243 EXPECT_THAT(uma_proto.structured_data().events().size(), 2);
244 }
245
TEST_F(StructuredMetricsServiceTest,SystemProfileFilled)246 TEST_F(StructuredMetricsServiceTest, SystemProfileFilled) {
247 Init();
248
249 EnableRecording();
250 EnableReporting();
251
252 TestEventOne().SetTestMetricTwo(1).Record();
253 TestEventSeven().SetTestMetricSeven(1).Record();
254
255 service_->Flush(metrics::MetricsLogsEventManager::CreateReason::kUnknown);
256
257 const auto uma_proto = GetPersistedLog();
258 EXPECT_THAT(uma_proto.structured_data().events().size(), 2);
259 EXPECT_TRUE(uma_proto.has_system_profile());
260
261 const SystemProfileProto& system_profile = uma_proto.system_profile();
262 EXPECT_EQ(system_profile.channel(), client_.GetChannel());
263 EXPECT_EQ(system_profile.app_version(), client_.GetVersionString());
264 }
265
TEST_F(StructuredMetricsServiceTest,DoesNotRecordWhenRecordingDisabled)266 TEST_F(StructuredMetricsServiceTest, DoesNotRecordWhenRecordingDisabled) {
267 Init();
268 EnableRecording();
269 EnableReporting();
270
271 TestEventOne().SetTestMetricTwo(1).Record();
272 TestEventSeven().SetTestMetricSeven(1).Record();
273
274 DisableRecording();
275
276 TestEventOne().SetTestMetricTwo(1).Record();
277 TestEventSeven().SetTestMetricSeven(1).Record();
278
279 EnableRecording();
280
281 service_->Flush(metrics::MetricsLogsEventManager::CreateReason::kUnknown);
282
283 const auto uma_proto = GetPersistedLog();
284 EXPECT_THAT(uma_proto.structured_data().events().size(), 2);
285 }
286
287 } // namespace metrics::structured
288