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_recorder.h"
6 
7 #include <cstdint>
8 #include <memory>
9 #include <utility>
10 
11 #include "base/files/file_path.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/test/metrics/histogram_tester.h"
16 #include "base/test/scoped_feature_list.h"
17 #include "base/test/task_environment.h"
18 #include "base/threading/scoped_blocking_call.h"
19 #include "components/metrics/structured/event.h"
20 #include "components/metrics/structured/proto/event_storage.pb.h"
21 #include "components/metrics/structured/recorder.h"
22 #include "components/metrics/structured/structured_events.h"
23 #include "components/metrics/structured/structured_metrics_client.h"
24 #include "components/metrics/structured/structured_metrics_features.h"
25 #include "components/metrics/structured/test/test_event_storage.h"
26 #include "components/metrics/structured/test/test_key_data_provider.h"
27 #include "testing/gtest/include/gtest/gtest.h"
28 #include "third_party/metrics_proto/chrome_user_metrics_extension.pb.h"
29 
30 namespace metrics::structured {
31 
32 namespace {
33 
34 // These project, event, and metric names are used for testing.
35 
36 // The name hash of "TestProjectOne".
37 constexpr uint64_t kProjectOneHash = UINT64_C(16881314472396226433);
38 // The name hash of "TestProjectTwo".
39 constexpr uint64_t kProjectTwoHash = UINT64_C(5876808001962504629);
40 // The name hash of "TestProjectThree".
41 constexpr uint64_t kProjectThreeHash = UINT64_C(10860358748803291132);
42 // The name hash of "TestProjectFour".
43 constexpr uint64_t kProjectFourHash = UINT64_C(6801665881746546626);
44 // The name hash of "TestProjectFive"
45 constexpr uint64_t kProjectFiveHash = UINT64_C(3960582687892677139);
46 // The name hash of "TestProjectSix"
47 constexpr uint64_t kProjectSixHash = UINT64_C(6972396123792667134);
48 // The name hash of "CrOSEvents"
49 constexpr uint64_t kCrOSEventsProjectHash = UINT64_C(12657197978410187837);
50 
51 // The name hash of "chrome::TestProjectOne::TestEventOne".
52 constexpr uint64_t kEventOneHash = UINT64_C(13593049295042080097);
53 // The name hash of "chrome::TestProjectTwo::TestEventTwo".
54 constexpr uint64_t kEventTwoHash = UINT64_C(8995967733561999410);
55 // The name hash of "chrome::TestProjectTwo::TestEventThree".
56 constexpr uint64_t kEventThreeHash = UINT64_C(5848687377041124372);
57 // The name hash of "chrome::TestProjectFour::TestEventFive".
58 constexpr uint64_t kEventFiveHash = UINT64_C(7045523601811399253);
59 // The name hash of "chrome::TestProjectFour::TestEventSix".
60 constexpr uint64_t kEventSixHash = UINT64_C(2873337042686447043);
61 // The name hash of "chrome::TestProjectSix::TestEventSeven".
62 constexpr uint64_t kEventSevenHash = UINT64_C(16749091071228286247);
63 // The name hash of "chrome::TestProjectSix::TestEnum".
64 constexpr uint64_t kEventEnumHash = UINT64_C(14837072141472316574);
65 // The name hash of "chrome::CrOSEvents::NoMetricsEvent".
66 constexpr uint64_t kNoMetricsEventHash = UINT64_C(5106854608989380457);
67 // The name has for "chrome::TestProjectSevent::TestEventEight".
68 const uint64_t kEventEightHash = UINT64_C(16290206418240617738);
69 
70 // The name hash of "TestMetricOne".
71 constexpr uint64_t kMetricOneHash = UINT64_C(637929385654885975);
72 // The name hash of "TestMetricTwo".
73 constexpr uint64_t kMetricTwoHash = UINT64_C(14083999144141567134);
74 // The name hash of "TestMetricThree".
75 constexpr uint64_t kMetricThreeHash = UINT64_C(13469300759843809564);
76 // The name hash of "TestMetricFive".
77 constexpr uint64_t kMetricFiveHash = UINT64_C(8665976921794972190);
78 // The name hash of "TestMetricSix".
79 constexpr uint64_t kMetricSixHash = UINT64_C(3431522567539822144);
80 // The name hash of "TestMetricSeven".
81 constexpr uint64_t kMetricSevenHash = UINT64_C(8395865158198697574);
82 // The name hash of "TestEnumMetric".
83 constexpr uint64_t kMetricEnumHash = UINT64_C(16584986597633634829);
84 
85 // The hex-encoded first 8 bytes of SHA256("aaa...a")
86 constexpr char kProjectOneId[] = "3BA3F5F43B926026";
87 // The hex-encoded first 8 bytes of SHA256("bbb...b")
88 constexpr char kProjectTwoId[] = "BDB339768BC5E4FE";
89 // The hex-encoded first 8 bytes of SHA256("ddd...d")
90 constexpr char kProjectFourId[] = "FBBBB6DE2AA74C3C";
91 
92 // Test values.
93 constexpr char kValueOne[] = "value one";
94 constexpr char kValueTwo[] = "value two";
95 
HashToHex(const uint64_t hash)96 std::string HashToHex(const uint64_t hash) {
97   return base::HexEncode(&hash, sizeof(uint64_t));
98 }
99 
100 class TestRecorder : public StructuredMetricsClient::RecordingDelegate {
101  public:
102   TestRecorder() = default;
103   TestRecorder(const TestRecorder& recorder) = delete;
104   TestRecorder& operator=(const TestRecorder& recorder) = delete;
105   ~TestRecorder() override = default;
106 
RecordEvent(Event && event)107   void RecordEvent(Event&& event) override {
108     Recorder::GetInstance()->RecordEvent(std::move(event));
109   }
110 
IsReadyToRecord() const111   bool IsReadyToRecord() const override { return true; }
112 };
113 
114 }  // namespace
115 
116 class TestStructuredMetricsRecorder : public StructuredMetricsRecorder {
117  public:
TestStructuredMetricsRecorder(const base::FilePath & device_key_path,const base::FilePath & profile_key_path)118   TestStructuredMetricsRecorder(const base::FilePath& device_key_path,
119                                 const base::FilePath& profile_key_path)
120       : StructuredMetricsRecorder(
121             std::make_unique<TestKeyDataProvider>(device_key_path,
122                                                   profile_key_path),
123             std::make_unique<TestEventStorage>()) {
124     test_key_data_provider_ =
125         static_cast<TestKeyDataProvider*>(key_data_provider());
126   }
127 
128   using StructuredMetricsRecorder::StructuredMetricsRecorder;
129 
OnProfileAdded(const base::FilePath & profile_path)130   void OnProfileAdded(const base::FilePath& profile_path) {
131     test_key_data_provider_->OnProfileAdded(profile_path);
132   }
133 
134  private:
135   raw_ptr<TestKeyDataProvider> test_key_data_provider_;
136 };
137 
138 class StructuredMetricsRecorderTest : public testing::Test {
139  protected:
SetUp()140   void SetUp() override {
141     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
142 
143     // Fixed paths to store keys for test.
144     device_key_path_ = temp_dir_.GetPath()
145                            .Append(FILE_PATH_LITERAL("structured_metrics"))
146                            .Append(FILE_PATH_LITERAL("device_keys"));
147     profile_key_path_ = temp_dir_.GetPath()
148                             .Append(FILE_PATH_LITERAL("structured_metrics"))
149                             .Append(FILE_PATH_LITERAL("profile_keys"));
150 
151     Recorder::GetInstance()->SetUiTaskRunner(
152         task_environment_.GetMainThreadTaskRunner());
153     StructuredMetricsClient::Get()->SetDelegate(&test_recorder_);
154     // Move the mock date forward from day 0, because KeyData assumes that day 0
155     // is a bug.
156     task_environment_.AdvanceClock(base::Days(1000));
157   }
158 
TempDirPath()159   base::FilePath TempDirPath() { return temp_dir_.GetPath(); }
160 
ProfileKeyFilePath()161   base::FilePath ProfileKeyFilePath() { return profile_key_path_; }
162 
DeviceKeyFilePath()163   base::FilePath DeviceKeyFilePath() { return device_key_path_; }
164 
TearDown()165   void TearDown() override { StructuredMetricsClient::Get()->UnsetDelegate(); }
166 
Wait()167   void Wait() { task_environment_.RunUntilIdle(); }
168 
169   // Adds a project to the disallowed projects list.
AddDisallowedProject(uint64_t project_name_hash)170   void AddDisallowedProject(uint64_t project_name_hash) {
171     recorder_->AddDisallowedProjectForTest(project_name_hash);
172   }
173 
WriteTestingProfileKeys()174   void WriteTestingProfileKeys() {
175     const int today = (base::Time::Now() - base::Time::UnixEpoch()).InDays();
176 
177     KeyDataProto proto;
178     KeyProto& key_one = (*proto.mutable_keys())[kProjectOneHash];
179     key_one.set_key("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
180     key_one.set_last_rotation(today);
181     key_one.set_rotation_period(90);
182 
183     KeyProto& key_two = (*proto.mutable_keys())[kProjectTwoHash];
184     key_two.set_key("bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb");
185     key_two.set_last_rotation(today);
186     key_two.set_rotation_period(90);
187 
188     KeyProto& key_three = (*proto.mutable_keys())[kProjectThreeHash];
189     key_three.set_key("cccccccccccccccccccccccccccccccc");
190     key_three.set_last_rotation(today);
191     key_three.set_rotation_period(90);
192 
193     KeyProto& cros_events = (*proto.mutable_keys())[kCrOSEventsProjectHash];
194     cros_events.set_key("cccccccccccccccccccccccccccccccc");
195     cros_events.set_last_rotation(today);
196     cros_events.set_rotation_period(90);
197 
198     base::CreateDirectory(ProfileKeyFilePath().DirName());
199     ASSERT_TRUE(
200         base::WriteFile(ProfileKeyFilePath(), proto.SerializeAsString()));
201     Wait();
202   }
203 
WriteTestingDeviceKeys()204   void WriteTestingDeviceKeys() {
205     const int today = (base::Time::Now() - base::Time::UnixEpoch()).InDays();
206 
207     KeyDataProto proto;
208     KeyProto& key = (*proto.mutable_keys())[kProjectFourHash];
209     key.set_key("dddddddddddddddddddddddddddddddd");
210     key.set_last_rotation(today);
211     key.set_rotation_period(90);
212 
213     base::CreateDirectory(DeviceKeyFilePath().DirName());
214     ASSERT_TRUE(
215         base::WriteFile(DeviceKeyFilePath(), proto.SerializeAsString()));
216     Wait();
217   }
218 
ReadKeys(const base::FilePath & filepath)219   KeyDataProto ReadKeys(const base::FilePath& filepath) {
220     base::ScopedBlockingCall scoped_blocking_call(
221         FROM_HERE, base::BlockingType::MAY_BLOCK);
222     Wait();
223     CHECK(base::PathExists(filepath));
224 
225     std::string proto_str;
226     CHECK(base::ReadFileToString(filepath, &proto_str));
227 
228     KeyDataProto proto;
229     CHECK(proto.ParseFromString(proto_str));
230     return proto;
231   }
232 
233   // Simulates the three external events that the structure metrics system cares
234   // about: the metrics service initializing and enabling its providers, and a
235   // user logging in.
Init()236   void Init() {
237     // Create the provider, normally done by the ChromeMetricsServiceClient.
238     recorder_ = std::make_unique<TestStructuredMetricsRecorder>(
239         device_key_path_, profile_key_path_);
240     // Enable recording, normally done after the metrics service has checked
241     // consent allows recording.
242     recorder_->EnableRecording();
243     // Add a profile, normally done by the ChromeMetricsServiceClient after a
244     // user logs in.
245     recorder_->OnProfileAdded(TempDirPath());
246     Wait();
247   }
248 
249   // Enables recording without adding a profile.
InitWithoutLogin()250   void InitWithoutLogin() {
251     // Create the provider, normally done by the ChromeMetricsServiceClient.
252     recorder_ = std::make_unique<TestStructuredMetricsRecorder>(
253         device_key_path_, profile_key_path_);
254     // Enable recording, normally done after the metrics service has checked
255     // consent allows recording.
256     recorder_->EnableRecording();
257   }
258 
259   // Sets up StructuredMetricsRecorder.
InitWithoutEnabling()260   void InitWithoutEnabling() {
261     // Create the provider, normally done by the ChromeMetricsServiceClient.
262     recorder_ = std::make_unique<TestStructuredMetricsRecorder>(
263         device_key_path_, profile_key_path_);
264   }
265 
is_initialized()266   bool is_initialized() { return recorder_->IsInitialized(); }
267 
is_recording_enabled()268   bool is_recording_enabled() { return recorder_->recording_enabled_; }
269 
OnRecordingEnabled()270   void OnRecordingEnabled() { recorder_->EnableRecording(); }
271 
OnRecordingDisabled()272   void OnRecordingDisabled() { recorder_->DisableRecording(); }
273 
OnProfileAdded(const base::FilePath & path)274   void OnProfileAdded(const base::FilePath& path) {
275     recorder_->OnProfileAdded(path);
276   }
277 
GetUMAEventMetrics()278   StructuredDataProto GetUMAEventMetrics() {
279     ChromeUserMetricsExtension uma_proto;
280     recorder_->ProvideUmaEventMetrics(uma_proto);
281     Wait();
282     return uma_proto.structured_data();
283   }
284 
GetEventMetrics()285   StructuredDataProto GetEventMetrics() {
286     ChromeUserMetricsExtension uma_proto;
287     recorder_->ProvideEventMetrics(uma_proto);
288     Wait();
289     return uma_proto.structured_data();
290   }
291 
ExpectNoErrors()292   void ExpectNoErrors() {
293     histogram_tester_.ExpectTotalCount("UMA.StructuredMetrics.InternalError",
294                                        0);
295   }
296 
297  protected:
298   std::unique_ptr<TestStructuredMetricsRecorder> recorder_;
299   // Feature list should be constructed before task environment.
300   base::test::ScopedFeatureList scoped_feature_list_;
301   base::test::TaskEnvironment task_environment_{
302       base::test::TaskEnvironment::MainThreadType::UI,
303       base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED,
304       base::test::TaskEnvironment::TimeSource::MOCK_TIME};
305   base::HistogramTester histogram_tester_;
306   base::ScopedTempDir temp_dir_;
307 
308  private:
309   TestRecorder test_recorder_;
310 
311   base::FilePath device_key_path_;
312   base::FilePath profile_key_path_;
313 };
314 
315 // Simple test to ensure initialization works correctly in the case of a
316 // first-time run.
TEST_F(StructuredMetricsRecorderTest,RecorderInitializesFromBlankSlate)317 TEST_F(StructuredMetricsRecorderTest, RecorderInitializesFromBlankSlate) {
318   Init();
319   EXPECT_TRUE(is_initialized());
320   EXPECT_TRUE(is_recording_enabled());
321   ExpectNoErrors();
322 }
323 
324 // Ensure a call to OnRecordingDisabled prevents reporting.
TEST_F(StructuredMetricsRecorderTest,EventsNotReportedWhenRecordingDisabled)325 TEST_F(StructuredMetricsRecorderTest, EventsNotReportedWhenRecordingDisabled) {
326   Init();
327   OnRecordingDisabled();
328   StructuredMetricsClient::Record(std::move(
329       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
330   StructuredMetricsClient::Record(std::move(
331       events::v2::test_project_three::TestEventFour().SetTestMetricFour(1)));
332   EXPECT_EQ(GetUMAEventMetrics().events_size(), 0);
333   EXPECT_EQ(GetEventMetrics().events_size(), 0);
334   ExpectNoErrors();
335 }
336 
337 // Ensure that disabling the structured metrics feature flag prevents all
338 // structured metrics reporting.
TEST_F(StructuredMetricsRecorderTest,EventsNotReportedWhenFeatureDisabled)339 TEST_F(StructuredMetricsRecorderTest, EventsNotReportedWhenFeatureDisabled) {
340   scoped_feature_list_.InitAndDisableFeature(features::kStructuredMetrics);
341 
342   Init();
343   // OnRecordingEnabled should not actually enable recording because the flag is
344   // disabled.
345   OnRecordingEnabled();
346   StructuredMetricsClient::Record(std::move(
347       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
348   StructuredMetricsClient::Record(std::move(
349       events::v2::test_project_three::TestEventFour().SetTestMetricFour(1)));
350 
351   EXPECT_EQ(GetUMAEventMetrics().events_size(), 0);
352   EXPECT_EQ(GetEventMetrics().events_size(), 0);
353   ExpectNoErrors();
354 }
355 
356 // Ensure that, if recording is disabled part-way through initialization, the
357 // initialization still completes correctly, but recording is correctly set to
358 // disabled.
TEST_F(StructuredMetricsRecorderTest,RecordingDisabledDuringInitialization)359 TEST_F(StructuredMetricsRecorderTest, RecordingDisabledDuringInitialization) {
360   InitWithoutEnabling();
361 
362   OnProfileAdded(TempDirPath());
363   OnRecordingDisabled();
364   EXPECT_FALSE(is_initialized());
365   EXPECT_FALSE(is_recording_enabled());
366 
367   Wait();
368   EXPECT_TRUE(is_initialized());
369   EXPECT_FALSE(is_recording_enabled());
370 
371   ExpectNoErrors();
372 }
373 
374 // Ensure that recording is disabled until explicitly enabled with a call to
375 // OnRecordingEnabled.
TEST_F(StructuredMetricsRecorderTest,RecordingDisabledByDefault)376 TEST_F(StructuredMetricsRecorderTest, RecordingDisabledByDefault) {
377   InitWithoutEnabling();
378 
379   OnProfileAdded(TempDirPath());
380   Wait();
381   EXPECT_TRUE(is_initialized());
382   EXPECT_FALSE(is_recording_enabled());
383 
384   OnRecordingEnabled();
385   EXPECT_TRUE(is_recording_enabled());
386 
387   ExpectNoErrors();
388 }
389 
TEST_F(StructuredMetricsRecorderTest,RecordedEventAppearsInReport)390 TEST_F(StructuredMetricsRecorderTest, RecordedEventAppearsInReport) {
391   Init();
392 
393   StructuredMetricsClient::Record(
394       std::move(events::v2::test_project_one::TestEventOne()
395                     .SetTestMetricOne("a string")
396                     .SetTestMetricTwo(12345)));
397   StructuredMetricsClient::Record(
398       std::move(events::v2::test_project_one::TestEventOne()
399                     .SetTestMetricOne("a string")
400                     .SetTestMetricTwo(12345)));
401   StructuredMetricsClient::Record(
402       std::move(events::v2::test_project_one::TestEventOne()
403                     .SetTestMetricOne("a string")
404                     .SetTestMetricTwo(12345)));
405 
406   EXPECT_EQ(GetUMAEventMetrics().events_size(), 0);
407   EXPECT_EQ(GetEventMetrics().events_size(), 3);
408   ExpectNoErrors();
409 }
410 
TEST_F(StructuredMetricsRecorderTest,EventMetricsReportedCorrectly)411 TEST_F(StructuredMetricsRecorderTest, EventMetricsReportedCorrectly) {
412   WriteTestingProfileKeys();
413   Init();
414 
415   StructuredMetricsClient::Record(
416       std::move(events::v2::test_project_one::TestEventOne()
417                     .SetTestMetricOne(kValueOne)
418                     .SetTestMetricTwo(12345)));
419   StructuredMetricsClient::Record(
420       std::move(events::v2::test_project_two::TestEventTwo().SetTestMetricThree(
421           kValueTwo)));
422 
423   const auto data = GetEventMetrics();
424   ASSERT_EQ(data.events_size(), 2);
425 
426   {  // First event
427     const auto& event = data.events(0);
428     EXPECT_EQ(event.event_name_hash(), kEventOneHash);
429     EXPECT_EQ(event.project_name_hash(), kProjectOneHash);
430     EXPECT_EQ(HashToHex(event.profile_event_id()), kProjectOneId);
431     ASSERT_EQ(event.metrics_size(), 2);
432 
433     {  // First metric
434       const auto& metric = event.metrics(0);
435       EXPECT_EQ(metric.name_hash(), kMetricOneHash);
436       EXPECT_EQ(HashToHex(metric.value_hmac()),
437                 // Value of HMAC_256("aaa...a", concat(hex(kMetricOneHash),
438                 // kValueOne))
439                 "8C2469269D142715");
440     }
441 
442     {  // Second metric
443       const auto& metric = event.metrics(1);
444       EXPECT_EQ(metric.name_hash(), kMetricTwoHash);
445       EXPECT_EQ(metric.value_int64(), 12345);
446     }
447   }
448 
449   {  // Second event
450     const auto& event = data.events(1);
451     EXPECT_EQ(event.event_name_hash(), kEventTwoHash);
452     EXPECT_EQ(event.project_name_hash(), kProjectTwoHash);
453     EXPECT_EQ(HashToHex(event.profile_event_id()), kProjectTwoId);
454     ASSERT_EQ(event.metrics_size(), 1);
455 
456     {  // First metric
457       const auto& metric = event.metrics(0);
458       EXPECT_EQ(metric.name_hash(), kMetricThreeHash);
459       EXPECT_EQ(HashToHex(metric.value_hmac()),
460                 // Value of HMAC_256("bbb...b", concat(hex(kProjectTwoHash),
461                 // kValueTwo))
462                 "86F0169868588DC7");
463     }
464   }
465 
466   histogram_tester_.ExpectTotalCount("UMA.StructuredMetrics.InternalError", 0);
467 }
468 
469 // Ensure that events containing raw string metrics are reported correctly.
TEST_F(StructuredMetricsRecorderTest,RawStringMetricsReportedCorrectly)470 TEST_F(StructuredMetricsRecorderTest, RawStringMetricsReportedCorrectly) {
471   Init();
472 
473   const std::string test_string = "a raw string value";
474   StructuredMetricsClient::Record(
475       std::move(events::v2::test_project_five::TestEventSix().SetTestMetricSix(
476           test_string)));
477 
478   const auto data = GetEventMetrics();
479   ASSERT_EQ(data.events_size(), 1);
480 
481   const auto& event = data.events(0);
482   EXPECT_EQ(event.event_name_hash(), kEventSixHash);
483   EXPECT_EQ(event.project_name_hash(), kProjectFiveHash);
484   EXPECT_FALSE(event.has_profile_event_id());
485   EXPECT_EQ(event.event_type(), StructuredEventProto_EventType_RAW_STRING);
486 
487   ASSERT_EQ(event.metrics_size(), 1);
488   const auto& metric = event.metrics(0);
489 
490   EXPECT_EQ(metric.name_hash(), kMetricSixHash);
491   EXPECT_EQ(metric.value_string(), test_string);
492 }
493 
TEST_F(StructuredMetricsRecorderTest,FloatMetricsReportedCorrectly)494 TEST_F(StructuredMetricsRecorderTest, FloatMetricsReportedCorrectly) {
495   Init();
496 
497   const float test_float = 3.4;
498   const float test_float2 = 3.14e-8;
499 
500   StructuredMetricsClient::Record(std::move(
501       events::v2::test_project_six::TestEventSeven().SetTestMetricSeven(
502           test_float)));
503 
504   StructuredMetricsClient::Record(std::move(
505       events::v2::test_project_six::TestEventSeven().SetTestMetricSeven(
506           test_float2)));
507 
508   const auto data = GetEventMetrics();
509   ASSERT_EQ(data.events_size(), 2);
510 
511   const auto& event = data.events(0);
512   EXPECT_EQ(event.event_name_hash(), kEventSevenHash);
513   EXPECT_EQ(event.project_name_hash(), kProjectSixHash);
514   EXPECT_FALSE(event.has_profile_event_id());
515 
516   ASSERT_EQ(event.metrics_size(), 1);
517   const auto& metric = event.metrics(0);
518 
519   EXPECT_EQ(metric.name_hash(), kMetricSevenHash);
520   EXPECT_EQ(metric.value_double(), test_float);
521 
522   const auto& event2 = data.events(1);
523   EXPECT_EQ(event2.event_name_hash(), kEventSevenHash);
524   EXPECT_EQ(event2.project_name_hash(), kProjectSixHash);
525   EXPECT_FALSE(event2.has_profile_event_id());
526 
527   ASSERT_EQ(event2.metrics_size(), 1);
528   const auto& metric2 = event2.metrics(0);
529 
530   EXPECT_EQ(metric2.name_hash(), kMetricSevenHash);
531   EXPECT_EQ(metric2.value_double(), test_float2);
532 }
533 
534 // TODO: Test copied in AshStructuredMetricsRecorder unit tests, remove this one
535 // once the test key provider is simplified.
TEST_F(StructuredMetricsRecorderTest,DeviceKeysUsedForDeviceScopedProjects)536 TEST_F(StructuredMetricsRecorderTest, DeviceKeysUsedForDeviceScopedProjects) {
537   WriteTestingProfileKeys();
538   WriteTestingDeviceKeys();
539   Init();
540 
541   // This event's project has device scope set, so should use the per-device
542   // keys set by WriteTestingDeviceKeys. In this case the expected key is
543   // "ddd...d", which we observe by checking the ID and HMAC have the correct
544   // value given that key.
545   StructuredMetricsClient::Record(std::move(
546       events::v2::test_project_four::TestEventFive().SetTestMetricFive(
547           "value")));
548 
549   const auto data = GetEventMetrics();
550   ASSERT_EQ(data.events_size(), 1);
551 
552   const auto& event = data.events(0);
553   EXPECT_EQ(event.event_name_hash(), kEventFiveHash);
554   EXPECT_EQ(event.project_name_hash(), kProjectFourHash);
555   // The hex-encoded first 8 bytes of SHA256("ddd...d").
556   EXPECT_EQ(HashToHex(event.profile_event_id()), kProjectFourId);
557   ASSERT_EQ(event.metrics_size(), 1);
558 
559   const auto& metric = event.metrics(0);
560   EXPECT_EQ(metric.name_hash(), kMetricFiveHash);
561   EXPECT_EQ(HashToHex(metric.value_hmac()),
562             // Value of HMAC_256("ddd...d", concat(hex(kMetricFiveHash),
563             // "value"))
564             "4CC202FAA78FDC7A");
565 
566   histogram_tester_.ExpectTotalCount("UMA.StructuredMetrics.InternalError", 0);
567 }
568 
569 // Check that a full int64 can be recorded, and is not truncated to an int32.
TEST_F(StructuredMetricsRecorderTest,Int64MetricsNotTruncated)570 TEST_F(StructuredMetricsRecorderTest, Int64MetricsNotTruncated) {
571   Init();
572   const int64_t big = 1ll << 60;
573   StructuredMetricsClient::Record(std::move(
574       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(big)));
575 
576   const auto data = GetEventMetrics();
577   ASSERT_EQ(data.events_size(), 1);
578   const auto& event = data.events(0);
579   ASSERT_EQ(event.metrics_size(), 1);
580   const auto& metric = event.metrics(0);
581   EXPECT_EQ(metric.value_int64(), big);
582 }
583 
TEST_F(StructuredMetricsRecorderTest,EventsWithinProjectReportedWithSameID)584 TEST_F(StructuredMetricsRecorderTest, EventsWithinProjectReportedWithSameID) {
585   WriteTestingProfileKeys();
586   Init();
587 
588   StructuredMetricsClient::Record(
589       std::move(events::v2::test_project_one::TestEventOne()));
590   StructuredMetricsClient::Record(
591       std::move(events::v2::test_project_two::TestEventTwo()));
592   StructuredMetricsClient::Record(
593       std::move(events::v2::test_project_two::TestEventThree()));
594 
595   const auto data = GetEventMetrics();
596   ASSERT_EQ(data.events_size(), 3);
597 
598   const auto& event_one = data.events(0);
599   const auto& event_two = data.events(1);
600   const auto& event_three = data.events(2);
601 
602   // Check events are in the right order.
603   EXPECT_EQ(event_one.event_name_hash(), kEventOneHash);
604   EXPECT_EQ(event_two.event_name_hash(), kEventTwoHash);
605   EXPECT_EQ(event_three.event_name_hash(), kEventThreeHash);
606 
607   // Events two and three share a project, so should have the same project
608   // name hash. Event one should have its own project name hash.
609   EXPECT_EQ(event_one.project_name_hash(), kProjectOneHash);
610   EXPECT_EQ(event_two.project_name_hash(), kProjectTwoHash);
611   EXPECT_EQ(event_three.project_name_hash(), kProjectTwoHash);
612 
613   // Events two and three share a project, so should have the same ID. Event
614   // one should have its own ID.
615   EXPECT_EQ(HashToHex(event_one.profile_event_id()), kProjectOneId);
616   EXPECT_EQ(HashToHex(event_two.profile_event_id()), kProjectTwoId);
617   EXPECT_EQ(HashToHex(event_three.profile_event_id()), kProjectTwoId);
618 
619   histogram_tester_.ExpectTotalCount("UMA.StructuredMetrics.InternalError", 0);
620 }
621 
TEST_F(StructuredMetricsRecorderTest,EventWithoutMetricsReportCorrectly)622 TEST_F(StructuredMetricsRecorderTest, EventWithoutMetricsReportCorrectly) {
623   Init();
624 
625   const int test_time = 50;
626 
627   events::v2::cr_os_events::NoMetricsEvent test_event;
628   EXPECT_TRUE(test_event.IsEventSequenceType());
629   test_event.SetEventSequenceMetadata(Event::EventSequenceMetadata(1));
630   test_event.SetRecordedTimeSinceBoot(base::Milliseconds(test_time));
631   StructuredMetricsClient::Record(std::move(test_event));
632 
633   const auto data = GetEventMetrics();
634 
635   EXPECT_EQ(GetUMAEventMetrics().events_size(), 0);
636   EXPECT_EQ(data.events_size(), 1);
637 
638   const auto& event = data.events(0);
639 
640   EXPECT_EQ(event.project_name_hash(), kCrOSEventsProjectHash);
641   EXPECT_EQ(event.event_name_hash(), kNoMetricsEventHash);
642 }
643 
644 // Test that events reported before recording is enabled are ignored.
TEST_F(StructuredMetricsRecorderTest,EventsNotRecordedBeforeRecordingEnabled)645 TEST_F(StructuredMetricsRecorderTest, EventsNotRecordedBeforeRecordingEnabled) {
646   // Manually create and initialize the provider, adding recording calls between
647   // each step. All of these events should be ignored.
648   StructuredMetricsClient::Record(std::move(
649       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
650   InitWithoutEnabling();
651   StructuredMetricsClient::Record(std::move(
652       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
653   OnRecordingEnabled();
654   Wait();
655 
656   EXPECT_EQ(GetUMAEventMetrics().events_size(), 0);
657   EXPECT_EQ(GetEventMetrics().events_size(), 0);
658 
659   ExpectNoErrors();
660 }
661 
662 // Test that events reported after recording is enabled but before the keys are
663 // loaded are hashed and stored after keys are loaded.
TEST_F(StructuredMetricsRecorderTest,EventsRecordedBeforeKeysInitialized)664 TEST_F(StructuredMetricsRecorderTest, EventsRecordedBeforeKeysInitialized) {
665   InitWithoutLogin();
666   // Emulate metric before login.
667   StructuredMetricsClient::Record(std::move(
668       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
669 
670   OnProfileAdded(TempDirPath());
671 
672   // Called before user key is loaded.
673   StructuredMetricsClient::Record(std::move(
674       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
675   Wait();
676 
677   EXPECT_EQ(GetUMAEventMetrics().events_size(), 0);
678   EXPECT_EQ(GetEventMetrics().events_size(), 2);
679 
680   ExpectNoErrors();
681 }
682 
683 // Ensure a call to OnRecordingDisabled not only prevents the reporting of new
684 // events, but also clears the cache of any existing events that haven't yet
685 // been reported.
TEST_F(StructuredMetricsRecorderTest,ExistingEventsClearedWhenRecordingDisabled)686 TEST_F(StructuredMetricsRecorderTest,
687        ExistingEventsClearedWhenRecordingDisabled) {
688   Init();
689   StructuredMetricsClient::Record(std::move(
690       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
691   StructuredMetricsClient::Record(std::move(
692       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
693   StructuredMetricsClient::Record(std::move(
694       events::v2::test_project_three::TestEventFour().SetTestMetricFour(1)));
695   OnRecordingDisabled();
696   StructuredMetricsClient::Record(std::move(
697       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
698   StructuredMetricsClient::Record(std::move(
699       events::v2::test_project_three::TestEventFour().SetTestMetricFour(1)));
700   EXPECT_EQ(GetUMAEventMetrics().events_size(), 0);
701   EXPECT_EQ(GetEventMetrics().events_size(), 0);
702 
703   ExpectNoErrors();
704 }
705 
706 // Ensure that recording and reporting is re-enabled after recording is disabled
707 // and then enabled again.
TEST_F(StructuredMetricsRecorderTest,ReportingResumesWhenEnabled)708 TEST_F(StructuredMetricsRecorderTest, ReportingResumesWhenEnabled) {
709   Init();
710   StructuredMetricsClient::Record(std::move(
711       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
712   StructuredMetricsClient::Record(std::move(
713       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
714   StructuredMetricsClient::Record(std::move(
715       events::v2::test_project_two::TestEventThree().SetTestMetricFour(
716           "test-string")));
717 
718   OnRecordingDisabled();
719   OnRecordingEnabled();
720 
721   StructuredMetricsClient::Record(std::move(
722       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
723   StructuredMetricsClient::Record(std::move(
724       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
725   StructuredMetricsClient::Record(std::move(
726       events::v2::test_project_two::TestEventThree().SetTestMetricFour(
727           "test-string")));
728 
729   EXPECT_EQ(GetUMAEventMetrics().events_size(), 0);
730   EXPECT_EQ(GetEventMetrics().events_size(), 6);
731 
732   ExpectNoErrors();
733 }
734 
735 // Ensure that a call to ProvideCurrentSessionData before initialization
736 // completes returns no events.
TEST_F(StructuredMetricsRecorderTest,ReportsNothingBeforeInitializationComplete)737 TEST_F(StructuredMetricsRecorderTest,
738        ReportsNothingBeforeInitializationComplete) {
739   InitWithoutEnabling();
740 
741   EXPECT_EQ(GetUMAEventMetrics().events_size(), 0);
742   EXPECT_EQ(GetEventMetrics().events_size(), 0);
743   OnRecordingEnabled();
744   EXPECT_EQ(GetUMAEventMetrics().events_size(), 0);
745   EXPECT_EQ(GetEventMetrics().events_size(), 0);
746   OnProfileAdded(TempDirPath());
747   EXPECT_EQ(GetUMAEventMetrics().events_size(), 0);
748   EXPECT_EQ(GetEventMetrics().events_size(), 0);
749 }
750 
TEST_F(StructuredMetricsRecorderTest,EventsClone)751 TEST_F(StructuredMetricsRecorderTest, EventsClone) {
752   Init();
753 
754   events::v2::cr_os_events::Test1 event;
755 
756   const int test_time = 50;
757   const double test_metric = 1.0;
758 
759   event.SetEventSequenceMetadata(Event::EventSequenceMetadata(1));
760   event.SetRecordedTimeSinceBoot(base::Milliseconds(test_time));
761   event.SetMetric1(test_metric);
762 
763   auto cloned_event = event.Clone();
764 
765   EXPECT_EQ(event.event_sequence_metadata().reset_counter,
766             cloned_event.event_sequence_metadata().reset_counter);
767   EXPECT_EQ(event.project_name(), cloned_event.project_name());
768   EXPECT_EQ(event.event_name(), cloned_event.event_name());
769   EXPECT_EQ(event.is_event_sequence(), cloned_event.is_event_sequence());
770   EXPECT_EQ(event.recorded_time_since_boot(),
771             cloned_event.recorded_time_since_boot());
772   EXPECT_EQ(event.metric_values(), cloned_event.metric_values());
773 }
774 
TEST_F(StructuredMetricsRecorderTest,DisallowedProjectAreDropped)775 TEST_F(StructuredMetricsRecorderTest, DisallowedProjectAreDropped) {
776   Init();
777 
778   AddDisallowedProject(kProjectOneHash);
779 
780   StructuredMetricsClient::Record(std::move(
781       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
782   StructuredMetricsClient::Record(std::move(
783       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
784   StructuredMetricsClient::Record(std::move(
785       events::v2::test_project_two::TestEventThree().SetTestMetricFour(
786           "value")));
787 
788   const auto data = GetEventMetrics();
789   ASSERT_EQ(data.events_size(), 1);
790   ASSERT_EQ(data.events(0).project_name_hash(), kProjectTwoHash);
791 }
792 
793 class TestProcessor : public EventsProcessorInterface {
ShouldProcessOnEventRecord(const Event & event)794   bool ShouldProcessOnEventRecord(const Event& event) override { return true; }
795 
796   // no-op
OnEventsRecord(Event * event)797   void OnEventsRecord(Event* event) override {}
OnEventRecorded(StructuredEventProto * event)798   void OnEventRecorded(StructuredEventProto* event) override {}
799 
OnProvideIndependentMetrics(ChromeUserMetricsExtension * uma_proto)800   void OnProvideIndependentMetrics(
801       ChromeUserMetricsExtension* uma_proto) override {
802     uma_proto->mutable_structured_data()->set_is_device_enrolled(true);
803   }
804 };
805 
TEST_F(StructuredMetricsRecorderTest,AppliesProcessorCorrectly)806 TEST_F(StructuredMetricsRecorderTest, AppliesProcessorCorrectly) {
807   Init();
808 
809   // Processor that sets |is_device_enrolled| to true.
810   Recorder::GetInstance()->AddEventsProcessor(
811       std::make_unique<TestProcessor>());
812 
813   StructuredMetricsClient::Record(std::move(
814       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
815   const auto data = GetEventMetrics();
816 
817   EXPECT_TRUE(data.is_device_enrolled());
818 }
819 
TEST_F(StructuredMetricsRecorderTest,ForceRecordedEvents)820 TEST_F(StructuredMetricsRecorderTest, ForceRecordedEvents) {
821   // Init and disable recorder.
822   Init();
823   OnRecordingDisabled();
824 
825   StructuredMetricsClient::Record(
826       std::move(events::v2::test_project_seven::TestEventEight()));
827 
828   OnRecordingEnabled();
829   const auto data = GetEventMetrics();
830 
831   ASSERT_EQ(data.events_size(), 1);
832   ASSERT_EQ(data.events(0).event_name_hash(), kEventEightHash);
833 }
834 
TEST_F(StructuredMetricsRecorderTest,EventMetadataLookupCorrectly)835 TEST_F(StructuredMetricsRecorderTest, EventMetadataLookupCorrectly) {
836   constexpr std::string_view kProjectName = "TestProjectOne";
837   constexpr std::string_view kEventName = "TestEventOne";
838   constexpr std::string_view kMetricOneName = "TestMetricOne";
839   constexpr std::string_view kMetricTwoName = "TestMetricTwo";
840 
841   const validator::Validators* validators = validator::Validators::Get();
842 
843   ASSERT_EQ(validators->GetProjectName(kProjectOneHash), kProjectName);
844 
845   const auto* project_validator = validators->GetProjectValidator(kProjectName);
846   ASSERT_NE(project_validator, nullptr);
847 
848   ASSERT_EQ(project_validator->GetEventName(kEventOneHash), kEventName);
849 
850   const auto* event_validator =
851       project_validator->GetEventValidator(kEventName);
852   ASSERT_NE(event_validator, nullptr);
853 
854   ASSERT_EQ(event_validator->GetMetricName(kMetricOneHash), kMetricOneName);
855   ASSERT_EQ(event_validator->GetMetricName(kMetricTwoHash), kMetricTwoName);
856 }
857 
858 class TestWatcher : public StructuredMetricsRecorder::Observer {
859  public:
TestWatcher(uint64_t expected_event)860   TestWatcher(uint64_t expected_event) : expected_event_(expected_event) {}
861 
OnEventRecorded(const StructuredEventProto & event)862   void OnEventRecorded(const StructuredEventProto& event) override {
863     EXPECT_EQ(event.event_name_hash(), expected_event_);
864     ++event_count_;
865   }
866 
EventCount()867   int EventCount() { return event_count_; }
868 
869  private:
870   const uint64_t expected_event_;
871   int event_count_ = 0;
872 };
873 
TEST_F(StructuredMetricsRecorderTest,WatcherTest)874 TEST_F(StructuredMetricsRecorderTest, WatcherTest) {
875   Init();
876 
877   TestWatcher watcher(kEventOneHash);
878 
879   recorder_->AddEventsObserver(&watcher);
880 
881   StructuredMetricsClient::Record(
882       std::move(events::v2::test_project_one::TestEventOne()
883                     .SetTestMetricOne(kValueOne)
884                     .SetTestMetricTwo(12345)));
885 
886   Wait();
887 
888   EXPECT_EQ(watcher.EventCount(), 1);
889 
890   recorder_->RemoveEventsObserver(&watcher);
891 }
892 
TEST_F(StructuredMetricsRecorderTest,EnumRecordedCorrectly)893 TEST_F(StructuredMetricsRecorderTest, EnumRecordedCorrectly) {
894   Init();
895 
896   // Processor that sets |is_device_enrolled| to true.
897   StructuredMetricsClient::Record(
898       std::move(events::v2::test_project_six::TestEnum().SetTestEnumMetric(
899           events::v2::test_project_six::Enum1::VARIANT2)));
900   const auto data = GetEventMetrics();
901 
902   EXPECT_EQ(data.events_size(), 1);
903   EXPECT_EQ(data.events(0).project_name_hash(), kProjectSixHash);
904   EXPECT_EQ(data.events(0).event_name_hash(), kEventEnumHash);
905   EXPECT_EQ(data.events(0).metrics_size(), 1);
906   EXPECT_EQ(data.events(0).metrics(0).name_hash(), kMetricEnumHash);
907   EXPECT_EQ(data.events(0).metrics(0).value_int64(),
908             (int64_t)events::v2::test_project_six::Enum1::VARIANT2);
909 }
910 
TEST_F(StructuredMetricsRecorderTest,MultipleReports)911 TEST_F(StructuredMetricsRecorderTest, MultipleReports) {
912   Init();
913 
914   StructuredMetricsClient::Record(std::move(
915       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
916   StructuredMetricsClient::Record(std::move(
917       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
918   StructuredMetricsClient::Record(std::move(
919       events::v2::test_project_two::TestEventThree().SetTestMetricFour(
920           "test-string")));
921 
922   const auto data1 = GetEventMetrics();
923   EXPECT_EQ(data1.events_size(), 3);
924 
925   StructuredMetricsClient::Record(std::move(
926       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
927   StructuredMetricsClient::Record(std::move(
928       events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1)));
929   StructuredMetricsClient::Record(std::move(
930       events::v2::test_project_two::TestEventThree().SetTestMetricFour(
931           "test-string")));
932 
933   const auto data2 = GetEventMetrics();
934   EXPECT_EQ(data2.events_size(), 3);
935 }
936 
937 }  // namespace metrics::structured
938