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