1 // Copyright 2019 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_provider.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/bind.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/recorder.h"
21 #include "components/metrics/structured/storage.pb.h"
22 #include "components/metrics/structured/structured_events.h"
23 #include "components/metrics/structured/structured_metrics_features.h"
24 #include "components/metrics/structured/structured_metrics_recorder.h"
25 #include "components/metrics/structured/test/test_event_storage.h"
26 #include "components/metrics/structured/test/test_key_data_provider.h"
27 #include "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 constexpr char kHwid[] = "hwid";
35 constexpr size_t kUserCount = 3;
36
37 class TestRecorder : public StructuredMetricsClient::RecordingDelegate {
38 public:
39 TestRecorder() = default;
40 TestRecorder(const TestRecorder& recorder) = delete;
41 TestRecorder& operator=(const TestRecorder& recorder) = delete;
42 ~TestRecorder() override = default;
43
RecordEvent(Event && event)44 void RecordEvent(Event&& event) override {
45 Recorder::GetInstance()->RecordEvent(std::move(event));
46 }
47
IsReadyToRecord() const48 bool IsReadyToRecord() const override { return true; }
49 };
50
51 class TestSystemProfileProvider : public metrics::MetricsProvider {
52 public:
53 TestSystemProfileProvider() = default;
54 TestSystemProfileProvider(const TestSystemProfileProvider& recorder) = delete;
55 TestSystemProfileProvider& operator=(
56 const TestSystemProfileProvider& recorder) = delete;
57 ~TestSystemProfileProvider() override = default;
58
ProvideSystemProfileMetrics(metrics::SystemProfileProto * proto)59 void ProvideSystemProfileMetrics(
60 metrics::SystemProfileProto* proto) override {
61 proto->set_multi_profile_user_count(kUserCount);
62 proto->mutable_hardware()->set_full_hardware_class(kHwid);
63 }
64 };
65
66 } // namespace
67
68 class StructuredMetricsProviderTest : public testing::Test {
69 protected:
SetUp()70 void SetUp() override {
71 // Provider is being deprecated and new features are breaking the provider.
72 GTEST_SKIP();
73
74 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
75 Recorder::GetInstance()->SetUiTaskRunner(
76 task_environment_.GetMainThreadTaskRunner());
77 StructuredMetricsClient::Get()->SetDelegate(&recorder_);
78 // Move the mock date forward from day 0, because KeyData assumes that day 0
79 // is a bug.
80 task_environment_.AdvanceClock(base::Days(1000));
81
82 scoped_feature_list_.InitAndDisableFeature(
83 kEnabledStructuredMetricsService);
84 }
85
TearDown()86 void TearDown() override { StructuredMetricsClient::Get()->UnsetDelegate(); }
87
TempDirPath()88 base::FilePath TempDirPath() { return temp_dir_.GetPath(); }
89
ProfileKeyFilePath()90 base::FilePath ProfileKeyFilePath() {
91 return temp_dir_.GetPath()
92 .Append(FILE_PATH_LITERAL("structured_metrics"))
93 .Append(FILE_PATH_LITERAL("keys"));
94 }
95
DeviceKeyFilePath()96 base::FilePath DeviceKeyFilePath() {
97 return temp_dir_.GetPath()
98 .Append(FILE_PATH_LITERAL("structured_metrics"))
99 .Append(FILE_PATH_LITERAL("device_keys"));
100 }
101
Wait()102 void Wait() { task_environment_.RunUntilIdle(); }
103
104 // Simulates the three external events that the structure metrics system cares
105 // about: the metrics service initializing and enabling its providers, and a
106 // user logging in.
Init()107 void Init() {
108 // Create a system profile, normally done by ChromeMetricsServiceClient.
109 structured_metrics_recorder_ = std::make_unique<StructuredMetricsRecorder>(
110 std::make_unique<TestKeyDataProvider>(DeviceKeyFilePath(),
111 ProfileKeyFilePath()),
112 std::make_unique<TestEventStorage>());
113 // Create the provider, normally done by the ChromeMetricsServiceClient.
114 provider_ = std::unique_ptr<StructuredMetricsProvider>(
115 new StructuredMetricsProvider(
116 /*min_independent_metrics_interval=*/base::Seconds(0),
117 structured_metrics_recorder_.get()));
118 // Enable recording, normally done after the metrics service has checked
119 // consent allows recording.
120 provider_->OnRecordingEnabled();
121 }
122
OnRecordingEnabled()123 void OnRecordingEnabled() { provider_->OnRecordingEnabled(); }
124
OnProfileAdded(const base::FilePath & path)125 void OnProfileAdded(const base::FilePath& path) {
126 provider_->recorder().OnProfileAdded(path);
127 }
128
GetSessionData()129 StructuredDataProto GetSessionData() {
130 ChromeUserMetricsExtension uma_proto;
131 provider_->ProvideCurrentSessionData(&uma_proto);
132 Wait();
133 return uma_proto.structured_data();
134 }
135
GetIndependentMetrics()136 StructuredDataProto GetIndependentMetrics() {
137 ChromeUserMetricsExtension uma_proto;
138 if (provider_->HasIndependentMetrics()) {
139 provider_->ProvideIndependentMetrics(
140 base::DoNothing(),
141 base::BindOnce([](bool success) { CHECK(success); }), &uma_proto,
142 nullptr);
143 Wait();
144 return uma_proto.structured_data();
145 }
146
147 auto p = StructuredDataProto();
148 return p;
149 }
150
ExpectNoErrors()151 void ExpectNoErrors() {
152 histogram_tester_.ExpectTotalCount("UMA.StructuredMetrics.InternalError",
153 0);
154 }
155
RecorderInitialized()156 bool RecorderInitialized() { return provider_->recorder().IsInitialized(); }
157
158 protected:
159 std::unique_ptr<TestSystemProfileProvider> system_profile_provider_;
160 std::unique_ptr<StructuredMetricsRecorder> structured_metrics_recorder_;
161 std::unique_ptr<StructuredMetricsProvider> provider_;
162 // Feature list should be constructed before task environment.
163 base::test::ScopedFeatureList scoped_feature_list_;
164 base::test::TaskEnvironment task_environment_{
165 base::test::TaskEnvironment::MainThreadType::UI,
166 base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED,
167 base::test::TaskEnvironment::TimeSource::MOCK_TIME};
168 base::HistogramTester histogram_tester_;
169 base::ScopedTempDir temp_dir_;
170
171 private:
172 TestRecorder recorder_;
173 };
174
175 // Ensure that disabling independent upload of non-client_id metrics via feature
176 // flag instead uploads them in the main UMA upload.
TEST_F(StructuredMetricsProviderTest,DisableIndependentUploads)177 TEST_F(StructuredMetricsProviderTest, DisableIndependentUploads) {
178 base::test::ScopedFeatureList scoped_feature_list;
179 scoped_feature_list.InitAndEnableFeatureWithParameters(
180 features::kStructuredMetrics,
181 {{"enable_independent_metrics_upload", "false"}});
182
183 Init();
184
185 // Add a profile, normally done by the ChromeMetricsServiceClient after a
186 // user logs in.
187 OnProfileAdded(TempDirPath());
188 Wait();
189
190 OnRecordingEnabled();
191 events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1).Record();
192 events::v2::test_project_three::TestEventFour().SetTestMetricFour(1).Record();
193 EXPECT_EQ(GetIndependentMetrics().events_size(), 0);
194 EXPECT_EQ(GetSessionData().events_size(), 2);
195 ExpectNoErrors();
196 }
197
TEST_F(StructuredMetricsProviderTest,NoIndependentUploadsBeforeInitialized)198 TEST_F(StructuredMetricsProviderTest, NoIndependentUploadsBeforeInitialized) {
199 Init();
200 // Verify the recorder is not initialized.
201 EXPECT_FALSE(RecorderInitialized());
202
203 events::v2::test_project_one::TestEventOne().SetTestMetricTwo(1).Record();
204 events::v2::test_project_three::TestEventFour().SetTestMetricFour(1).Record();
205 EXPECT_EQ(GetIndependentMetrics().events_size(), 0);
206 EXPECT_EQ(GetSessionData().events_size(), 0);
207 ExpectNoErrors();
208 }
209
210 } // namespace metrics::structured
211