• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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