1 // Copyright 2017 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/persistent_system_profile.h"
6
7 #include <memory>
8
9 #include "base/check_op.h"
10 #include "base/metrics/persistent_memory_allocator.h"
11 #include "base/rand_util.h"
12 #include "components/variations/hashing.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 namespace metrics {
16
17 class PersistentSystemProfileTest : public testing::Test {
18 public:
19 const int32_t kAllocatorMemorySize = 1 << 20; // 1 MiB
20
21 PersistentSystemProfileTest() = default;
22
23 PersistentSystemProfileTest(const PersistentSystemProfileTest&) = delete;
24 PersistentSystemProfileTest& operator=(const PersistentSystemProfileTest&) =
25 delete;
26
27 ~PersistentSystemProfileTest() override = default;
28
SetUp()29 void SetUp() override {
30 memory_allocator_ = std::make_unique<base::LocalPersistentMemoryAllocator>(
31 kAllocatorMemorySize, 0, "");
32 records_ = std::make_unique<PersistentSystemProfile::RecordAllocator>(
33 memory_allocator_.get());
34 persistent_profile_.RegisterPersistentAllocator(memory_allocator_.get());
35 }
36
TearDown()37 void TearDown() override {
38 persistent_profile_.DeregisterPersistentAllocator(memory_allocator_.get());
39 records_.reset();
40 memory_allocator_.reset();
41 }
42
WriteRecord(uint8_t type,std::string_view record)43 void WriteRecord(uint8_t type, std::string_view record) {
44 persistent_profile_.allocators_[0].Write(
45 static_cast<PersistentSystemProfile::RecordType>(type), record);
46 }
47
ReadRecord(uint8_t * type,std::string * record)48 bool ReadRecord(uint8_t* type, std::string* record) {
49 PersistentSystemProfile::RecordType rec_type;
50
51 bool success = records_->Read(&rec_type, record);
52 *type = rec_type; // Convert to uint8_t for testing.
53 return success;
54 }
55
memory_allocator()56 base::PersistentMemoryAllocator* memory_allocator() {
57 return memory_allocator_.get();
58 }
59
persistent_profile()60 PersistentSystemProfile* persistent_profile() { return &persistent_profile_; }
61
62 private:
63 PersistentSystemProfile persistent_profile_;
64 std::unique_ptr<base::PersistentMemoryAllocator> memory_allocator_;
65 std::unique_ptr<PersistentSystemProfile::RecordAllocator> records_;
66 };
67
TEST_F(PersistentSystemProfileTest,Create)68 TEST_F(PersistentSystemProfileTest, Create) {
69 uint32_t type;
70 base::PersistentMemoryAllocator::Iterator iter(memory_allocator());
71 base::PersistentMemoryAllocator::Reference ref = iter.GetNext(&type);
72 DCHECK(ref);
73 DCHECK_NE(0U, type);
74 }
75
TEST_F(PersistentSystemProfileTest,RecordSplitting)76 TEST_F(PersistentSystemProfileTest, RecordSplitting) {
77 const size_t kRecordSize = 100 << 10; // 100 KiB
78 std::string buffer(kRecordSize, '\0');
79 base::RandBytes(base::as_writable_byte_span(buffer));
80
81 WriteRecord(42, buffer);
82
83 uint8_t type;
84 std::string record;
85 ASSERT_TRUE(ReadRecord(&type, &record));
86 EXPECT_EQ(42U, type);
87 EXPECT_EQ(buffer, record);
88 }
89
TEST_F(PersistentSystemProfileTest,ProfileStorage)90 TEST_F(PersistentSystemProfileTest, ProfileStorage) {
91 SystemProfileProto proto1;
92 SystemProfileProto::FieldTrial* trial = proto1.add_field_trial();
93 trial->set_name_id(123);
94 trial->set_group_id(456);
95
96 persistent_profile()->SetSystemProfile(proto1, false);
97
98 SystemProfileProto proto2;
99 ASSERT_TRUE(PersistentSystemProfile::HasSystemProfile(*memory_allocator()));
100 ASSERT_TRUE(
101 PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &proto2));
102 ASSERT_EQ(1, proto2.field_trial_size());
103 EXPECT_EQ(123U, proto2.field_trial(0).name_id());
104 EXPECT_EQ(456U, proto2.field_trial(0).group_id());
105
106 // Check that the profile can be overwritten by another incomplete profile.
107
108 trial = proto1.add_field_trial();
109 trial->set_name_id(34);
110 trial->set_group_id(50);
111
112 persistent_profile()->SetSystemProfile(proto1, false);
113
114 ASSERT_TRUE(
115 PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &proto2));
116 ASSERT_EQ(2, proto2.field_trial_size());
117 EXPECT_EQ(123U, proto2.field_trial(0).name_id());
118 EXPECT_EQ(456U, proto2.field_trial(0).group_id());
119 EXPECT_EQ(34U, proto2.field_trial(1).name_id());
120 EXPECT_EQ(50U, proto2.field_trial(1).group_id());
121
122 // Check that the profile can be overwritten by a complete profile.
123
124 trial = proto1.add_field_trial();
125 trial->set_name_id(78);
126 trial->set_group_id(90);
127
128 persistent_profile()->SetSystemProfile(proto1, true);
129
130 ASSERT_TRUE(
131 PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &proto2));
132 ASSERT_EQ(3, proto2.field_trial_size());
133 EXPECT_EQ(123U, proto2.field_trial(0).name_id());
134 EXPECT_EQ(456U, proto2.field_trial(0).group_id());
135 EXPECT_EQ(34U, proto2.field_trial(1).name_id());
136 EXPECT_EQ(50U, proto2.field_trial(1).group_id());
137 EXPECT_EQ(78U, proto2.field_trial(2).name_id());
138 EXPECT_EQ(90U, proto2.field_trial(2).group_id());
139
140 // Check that the profile won't be overwritten by a new non-complete profile.
141
142 trial = proto1.add_field_trial();
143 trial->set_name_id(0xC0DE);
144 trial->set_group_id(0xFEED);
145
146 persistent_profile()->SetSystemProfile(proto1, false);
147
148 ASSERT_TRUE(
149 PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &proto2));
150 ASSERT_EQ(3, proto2.field_trial_size());
151 EXPECT_EQ(123U, proto2.field_trial(0).name_id());
152 EXPECT_EQ(456U, proto2.field_trial(0).group_id());
153 EXPECT_EQ(34U, proto2.field_trial(1).name_id());
154 EXPECT_EQ(50U, proto2.field_trial(1).group_id());
155 EXPECT_EQ(78U, proto2.field_trial(2).name_id());
156 EXPECT_EQ(90U, proto2.field_trial(2).group_id());
157 }
158
TEST_F(PersistentSystemProfileTest,ProfileExtensions)159 TEST_F(PersistentSystemProfileTest, ProfileExtensions) {
160 persistent_profile()->AddFieldTrial("sna", "foo");
161
162 SystemProfileProto fetched;
163 ASSERT_FALSE(
164 PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &fetched));
165
166 SystemProfileProto proto;
167 SystemProfileProto::FieldTrial* trial = proto.add_field_trial();
168 trial->set_name_id(123);
169 trial->set_group_id(456);
170
171 // The system profile should now start fresh. In practice, field trials should
172 // already be properly updated in subsequent system profiles.
173 persistent_profile()->SetSystemProfile(proto, false);
174 ASSERT_TRUE(
175 PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &fetched));
176 ASSERT_EQ(1, fetched.field_trial_size());
177 EXPECT_EQ(123U, fetched.field_trial(0).name_id());
178 EXPECT_EQ(456U, fetched.field_trial(0).group_id());
179
180 persistent_profile()->AddFieldTrial("foo", "bar");
181 ASSERT_TRUE(
182 PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &fetched));
183 ASSERT_EQ(2, fetched.field_trial_size());
184 EXPECT_EQ(123U, fetched.field_trial(0).name_id());
185 EXPECT_EQ(456U, fetched.field_trial(0).group_id());
186 EXPECT_EQ(variations::HashName("foo"), fetched.field_trial(1).name_id());
187 EXPECT_EQ(variations::HashName("bar"), fetched.field_trial(1).group_id());
188 }
189
TEST_F(PersistentSystemProfileTest,OverwriteFieldTrialsInProfile)190 TEST_F(PersistentSystemProfileTest, OverwriteFieldTrialsInProfile) {
191 // Set system profile with the field trial.
192 SystemProfileProto proto;
193 SystemProfileProto::FieldTrial* trial = proto.add_field_trial();
194 trial->set_name_id(variations::HashName("foo"));
195 trial->set_group_id(456);
196 persistent_profile()->SetSystemProfile(proto, false);
197
198 // Overwrite the same trial with different group.
199 persistent_profile()->AddFieldTrial("foo", "bar");
200
201 // The fetched profile should have the latest group name,
202 SystemProfileProto fetched;
203 ASSERT_TRUE(
204 PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &fetched));
205 ASSERT_EQ(1, fetched.field_trial_size());
206 EXPECT_EQ(variations::HashName("foo"), fetched.field_trial(0).name_id());
207 EXPECT_EQ(variations::HashName("bar"), fetched.field_trial(0).group_id());
208 }
209
TEST_F(PersistentSystemProfileTest,OverwriteFieldTrials)210 TEST_F(PersistentSystemProfileTest, OverwriteFieldTrials) {
211 // Set up a non-empty system profile.
212 SystemProfileProto proto;
213 proto.set_client_uuid("id");
214 persistent_profile()->SetSystemProfile(proto, false);
215
216 // Set and overwrite the same trial with different group.
217 persistent_profile()->AddFieldTrial("foo", "bar");
218 persistent_profile()->AddFieldTrial("foo", "bar2");
219
220 // The fetched profile should have the latest group name,
221 SystemProfileProto fetched;
222 ASSERT_TRUE(
223 PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &fetched));
224 ASSERT_EQ(1, fetched.field_trial_size());
225 EXPECT_EQ(variations::HashName("foo"), fetched.field_trial(0).name_id());
226 EXPECT_EQ(variations::HashName("bar2"), fetched.field_trial(0).group_id());
227 }
228
TEST_F(PersistentSystemProfileTest,DeleteFieldTrials)229 TEST_F(PersistentSystemProfileTest, DeleteFieldTrials) {
230 // Set up a non-empty system profile.
231 SystemProfileProto proto;
232 proto.set_client_uuid("id");
233 persistent_profile()->SetSystemProfile(proto, false);
234
235 // Set and delete the trial.
236 persistent_profile()->AddFieldTrial("foo", "bar");
237 persistent_profile()->RemoveFieldTrial("foo");
238
239 // The fetched profile should not have the deleted trial.
240 SystemProfileProto fetched;
241 ASSERT_TRUE(
242 PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &fetched));
243 ASSERT_EQ(0, fetched.field_trial_size());
244
245 // Reset the trial and the fetched profile should have the latest group name.
246 persistent_profile()->AddFieldTrial("foo", "bar2");
247 ASSERT_TRUE(
248 PersistentSystemProfile::GetSystemProfile(*memory_allocator(), &fetched));
249 ASSERT_EQ(1, fetched.field_trial_size());
250 EXPECT_EQ(variations::HashName("foo"), fetched.field_trial(0).name_id());
251 EXPECT_EQ(variations::HashName("bar2"), fetched.field_trial(0).group_id());
252 }
253
254 } // namespace metrics
255