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