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