xref: /aosp_15_r20/external/cronet/components/metrics/serialization/serialization_utils_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2014 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/serialization/serialization_utils.h"
6 
7 #include <stddef.h>
8 #include <stdint.h>
9 
10 #include "base/check.h"
11 #include "base/files/file.h"
12 #include "base/files/file_util.h"
13 #include "base/files/scoped_temp_dir.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/test/metrics/histogram_tester.h"
16 #include "components/metrics/serialization/metric_sample.h"
17 #include "testing/gmock/include/gmock/gmock.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 
20 namespace metrics {
21 namespace {
22 
23 using ::testing::IsEmpty;
24 
25 class SerializationUtilsTest : public testing::Test {
26  protected:
SerializationUtilsTest()27   SerializationUtilsTest() {
28     bool success = temporary_dir_.CreateUniqueTempDir();
29     if (success) {
30       base::FilePath dir_path = temporary_dir_.GetPath();
31       filename_ = dir_path.value() + "chromeossampletest";
32       filepath_ = base::FilePath(filename_);
33     }
34   }
35 
SetUp()36   void SetUp() override { base::DeleteFile(filepath_); }
37 
TestSerialization(MetricSample * sample)38   void TestSerialization(MetricSample* sample) {
39     std::string serialized(sample->ToString());
40     ASSERT_EQ('\0', serialized.back());
41     std::unique_ptr<MetricSample> deserialized =
42         SerializationUtils::ParseSample(serialized);
43     ASSERT_TRUE(deserialized);
44     EXPECT_TRUE(sample->IsEqual(*deserialized.get()));
45   }
46 
filename() const47   const std::string& filename() const { return filename_; }
filepath() const48   const base::FilePath& filepath() const { return filepath_; }
49 
50  private:
51   std::string filename_;
52   base::ScopedTempDir temporary_dir_;
53   base::FilePath filepath_;
54 };
55 
TEST_F(SerializationUtilsTest,CrashSerializeTest)56 TEST_F(SerializationUtilsTest, CrashSerializeTest) {
57   // Should work with both 1 and non-1 values
58   TestSerialization(MetricSample::CrashSample("test", /*num_samples=*/1).get());
59   TestSerialization(
60       MetricSample::CrashSample("test", /*num_samples=*/10).get());
61 }
62 
TEST_F(SerializationUtilsTest,HistogramSerializeTest)63 TEST_F(SerializationUtilsTest, HistogramSerializeTest) {
64   TestSerialization(MetricSample::HistogramSample(
65                         "myhist", /*sample=*/13, /*min=*/1, /*max=*/100,
66                         /*bucket_count=*/10, /*num_samples=*/1)
67                         .get());
68   TestSerialization(MetricSample::HistogramSample(
69                         "myhist", /*sample=*/13, /*min=*/1, /*max=*/100,
70                         /*bucket_count=*/10, /*num_samples=*/2)
71                         .get());
72 }
73 
TEST_F(SerializationUtilsTest,LinearSerializeTest)74 TEST_F(SerializationUtilsTest, LinearSerializeTest) {
75   TestSerialization(
76       MetricSample::LinearHistogramSample("linearhist", /*sample=*/12,
77                                           /*max=*/30, /*num_samples=*/1)
78           .get());
79   TestSerialization(
80       MetricSample::LinearHistogramSample("linearhist", /*sample=*/12,
81                                           /*max=*/30, /*num_samples=*/10)
82           .get());
83 }
84 
TEST_F(SerializationUtilsTest,SparseSerializeTest)85 TEST_F(SerializationUtilsTest, SparseSerializeTest) {
86   TestSerialization(MetricSample::SparseHistogramSample(
87                         "mysparse", /*sample=*/30, /*num_samples=*/1)
88                         .get());
89   TestSerialization(MetricSample::SparseHistogramSample(
90                         "mysparse", /*sample=*/30, /*num_samples=*/10)
91                         .get());
92 }
93 
TEST_F(SerializationUtilsTest,UserActionSerializeTest)94 TEST_F(SerializationUtilsTest, UserActionSerializeTest) {
95   TestSerialization(
96       MetricSample::UserActionSample("myaction", /*num_samples=*/1).get());
97   TestSerialization(
98       MetricSample::UserActionSample("myaction", /*num_samples=*/10).get());
99 }
100 
TEST_F(SerializationUtilsTest,InvalidCrashSerialize)101 TEST_F(SerializationUtilsTest, InvalidCrashSerialize) {
102   // No name
103   EXPECT_EQ(nullptr, MetricSample::ParseCrash(""));
104   // Empty name
105   EXPECT_EQ(nullptr, MetricSample::ParseCrash(" "));
106   // num_samples is not a number
107   EXPECT_EQ(nullptr, MetricSample::ParseCrash("kernel asdf"));
108   // Too many numbers
109   EXPECT_EQ(nullptr, MetricSample::ParseCrash("kernel 1 2"));
110   // Negative num_samples
111   EXPECT_EQ(nullptr, MetricSample::ParseCrash("kernel -1"));
112 }
113 
TEST_F(SerializationUtilsTest,InvalidHistogramSample)114 TEST_F(SerializationUtilsTest, InvalidHistogramSample) {
115   // Too few parts
116   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 2 3"));
117   // Too many parts
118   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 2 3 4 5 6"));
119   // Empty hist name
120   EXPECT_EQ(nullptr, MetricSample::ParseHistogram(" 1 2 3 4 5"));
121   // sample is not a number
122   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist a 2 3 4 5"));
123   // min is not a number
124   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 a 3 4 5"));
125   // max is not a number
126   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 2 a 4 5"));
127   // buckets is not a number
128   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 2 3 a 5"));
129   // num_samples is not a number
130   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 2 3 4 a"));
131   // Negative num_samples
132   EXPECT_EQ(nullptr, MetricSample::ParseHistogram("hist 1 2 3 4 -1"));
133 }
134 
TEST_F(SerializationUtilsTest,InvalidSparseHistogramSample)135 TEST_F(SerializationUtilsTest, InvalidSparseHistogramSample) {
136   // Too few fields
137   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram("name"));
138   // Too many fields
139   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram("name 1 2 3"));
140   // No name
141   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram(" 1 2"));
142   // Invalid sample
143   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram("name a 2"));
144   // Invalid num_samples
145   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram("name 1 a"));
146   // Negative num_samples
147   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram("name 1 -1"));
148 }
149 
TEST_F(SerializationUtilsTest,InvalidLinearHistogramSample)150 TEST_F(SerializationUtilsTest, InvalidLinearHistogramSample) {
151   // Too few fields
152   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram("name 1"));
153   // Too many fields
154   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram("name 1 2 3 4"));
155   // No name
156   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram(" 1 2 3"));
157   // Invalid sample
158   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram("name a 2 3"));
159   // Invalid max
160   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram("name 1 a 3"));
161   // Invalid num_samples
162   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram("name 1 2 a"));
163   // Negative num_samples
164   EXPECT_EQ(nullptr, MetricSample::ParseLinearHistogram("name 1 2 -1"));
165 }
166 
TEST_F(SerializationUtilsTest,InvalidUserAction)167 TEST_F(SerializationUtilsTest, InvalidUserAction) {
168   // Too few fields
169   EXPECT_EQ(nullptr, MetricSample::ParseUserAction(""));
170   // Too many fields
171   EXPECT_EQ(nullptr, MetricSample::ParseUserAction("name 1 2"));
172   // No name
173   EXPECT_EQ(nullptr, MetricSample::ParseUserAction(" 1"));
174   // Invalid num_samples
175   EXPECT_EQ(nullptr, MetricSample::ParseUserAction("name a"));
176   // Negative num_samples
177   EXPECT_EQ(nullptr, MetricSample::ParseUserAction("name -1"));
178 }
179 
TEST_F(SerializationUtilsTest,IllegalNameAreFilteredTest)180 TEST_F(SerializationUtilsTest, IllegalNameAreFilteredTest) {
181   std::unique_ptr<MetricSample> sample1 = MetricSample::SparseHistogramSample(
182       "no space", /*sample=*/10, /*num_samples=*/1);
183   std::unique_ptr<MetricSample> sample2 = MetricSample::LinearHistogramSample(
184       base::StringPrintf("here%cbhe", '\0'), /*sample=*/1, /*max=*/3,
185       /*num_samples=*/2);
186 
187   EXPECT_FALSE(
188       SerializationUtils::WriteMetricToFile(*sample1.get(), filename()));
189   EXPECT_FALSE(
190       SerializationUtils::WriteMetricToFile(*sample2.get(), filename()));
191   int64_t size = 0;
192 
193   ASSERT_TRUE(!PathExists(filepath()) || base::GetFileSize(filepath(), &size));
194 
195   EXPECT_EQ(0, size);
196 }
197 
TEST_F(SerializationUtilsTest,BadInputIsCaughtTest)198 TEST_F(SerializationUtilsTest, BadInputIsCaughtTest) {
199   std::string input(
200       base::StringPrintf("sparsehistogram%cname foo%c1", '\0', '\0'));
201   EXPECT_EQ(nullptr, MetricSample::ParseSparseHistogram(input).get());
202 }
203 
TEST_F(SerializationUtilsTest,MessageSeparatedByZero)204 TEST_F(SerializationUtilsTest, MessageSeparatedByZero) {
205   std::unique_ptr<MetricSample> crash =
206       MetricSample::CrashSample("mycrash", /*num_samples=*/10);
207 
208   SerializationUtils::WriteMetricToFile(*crash.get(), filename());
209   int64_t size = 0;
210   ASSERT_TRUE(base::GetFileSize(filepath(), &size));
211   // 4 bytes for the size
212   // 5 bytes for crash
213   // 1 byte for \0
214   // 7 bytes for mycrash
215   // 3 bytes for " 10"
216   // 1 byte for \0
217   // -> total of 21
218   EXPECT_EQ(size, 21);
219 }
220 
TEST_F(SerializationUtilsTest,MessagesTooLongAreDiscardedTest)221 TEST_F(SerializationUtilsTest, MessagesTooLongAreDiscardedTest) {
222   // Creates a message that is bigger than the maximum allowed size.
223   // As we are adding extra character (crash, \0s, etc), if the name is
224   // kMessageMaxLength long, it will be too long.
225   std::string name(SerializationUtils::kMessageMaxLength, 'c');
226 
227   std::unique_ptr<MetricSample> crash =
228       MetricSample::CrashSample(name, /*num_samples=*/10);
229   EXPECT_FALSE(SerializationUtils::WriteMetricToFile(*crash.get(), filename()));
230   int64_t size = 0;
231   ASSERT_TRUE(base::GetFileSize(filepath(), &size));
232   EXPECT_EQ(0, size);
233 }
234 
TEST_F(SerializationUtilsTest,ReadLongMessageTest)235 TEST_F(SerializationUtilsTest, ReadLongMessageTest) {
236   base::File test_file(filepath(),
237                        base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_APPEND);
238   std::string message(SerializationUtils::kMessageMaxLength + 1, 'c');
239 
240   int32_t message_size = message.length() + sizeof(int32_t);
241   test_file.WriteAtCurrentPos(reinterpret_cast<const char*>(&message_size),
242                               sizeof(message_size));
243   test_file.WriteAtCurrentPos(message.c_str(), message.length());
244   test_file.Close();
245 
246   std::unique_ptr<MetricSample> crash =
247       MetricSample::CrashSample("test", /*num_samples=*/10);
248   SerializationUtils::WriteMetricToFile(*crash.get(), filename());
249 
250   std::vector<std::unique_ptr<MetricSample>> samples;
251   SerializationUtils::ReadAndTruncateMetricsFromFile(filename(), &samples);
252   ASSERT_EQ(size_t(1), samples.size());
253   ASSERT_TRUE(samples[0].get() != nullptr);
254   EXPECT_TRUE(crash->IsEqual(*samples[0]));
255 }
256 
TEST_F(SerializationUtilsTest,NegativeLengthTest)257 TEST_F(SerializationUtilsTest, NegativeLengthTest) {
258   // This input is specifically constructed to yield a single crash sample when
259   // parsed by a buggy version of the code but fails to parse and doesn't yield
260   // samples when parsed by a correct implementation.
261   constexpr uint8_t kInput[] = {
262       // Length indicating that next length field is the negative one below.
263       // This sample is invalid as it contains more than three null bytes.
264       0x14,
265       0x00,
266       0x00,
267       0x00,
268       // Encoding of a valid crash sample.
269       0x0c,
270       0x00,
271       0x00,
272       0x00,
273       0x63,
274       0x72,
275       0x61,
276       0x73,
277       0x68,
278       0x00,
279       0x61,
280       0x00,
281       // Invalid sample that jumps past the negative length bytes below.
282       0x08,
283       0x00,
284       0x00,
285       0x00,
286       // This is -16 in two's complement interpretation, pointing to the valid
287       // crash sample before.
288       0xf0,
289       0xff,
290       0xff,
291       0xff,
292   };
293   ASSERT_TRUE(base::WriteFile(filepath(), base::make_span(kInput)));
294 
295   std::vector<std::unique_ptr<MetricSample>> samples;
296   SerializationUtils::ReadAndTruncateMetricsFromFile(filename(), &samples);
297   ASSERT_EQ(0U, samples.size());
298 }
299 
TEST_F(SerializationUtilsTest,WriteReadTest_TruncateFile)300 TEST_F(SerializationUtilsTest, WriteReadTest_TruncateFile) {
301   std::unique_ptr<MetricSample> hist = MetricSample::HistogramSample(
302       "myhist", /*sample=*/1, /*min=*/2, /*max=*/3, /*bucket_count=*/4,
303       /*num_samples=*/5);
304   std::unique_ptr<MetricSample> crash =
305       MetricSample::CrashSample("mycrash", /*num_samples=*/10);
306   std::unique_ptr<MetricSample> lhist = MetricSample::LinearHistogramSample(
307       "linear", /*sample=*/1, /*max=*/10, /*num_samples=*/10);
308   std::unique_ptr<MetricSample> shist = MetricSample::SparseHistogramSample(
309       "mysparse", /*sample=*/30, /*num_samples=*/10);
310   std::unique_ptr<MetricSample> action =
311       MetricSample::UserActionSample("myaction", /*num_samples=*/1);
312 
313   SerializationUtils::WriteMetricToFile(*hist.get(), filename());
314   SerializationUtils::WriteMetricToFile(*crash.get(), filename());
315   SerializationUtils::WriteMetricToFile(*lhist.get(), filename());
316   SerializationUtils::WriteMetricToFile(*shist.get(), filename());
317   SerializationUtils::WriteMetricToFile(*action.get(), filename());
318   std::vector<std::unique_ptr<MetricSample>> vect;
319   SerializationUtils::ReadAndTruncateMetricsFromFile(filename(), &vect);
320   // NOTE: Should *not* have an entry for each repeated sample.
321   ASSERT_EQ(vect.size(), size_t(5));
322   for (auto& sample : vect) {
323     ASSERT_NE(nullptr, sample.get());
324   }
325   EXPECT_TRUE(hist->IsEqual(*vect[0]));
326   EXPECT_TRUE(crash->IsEqual(*vect[1]));
327   EXPECT_TRUE(lhist->IsEqual(*vect[2]));
328   EXPECT_TRUE(shist->IsEqual(*vect[3]));
329   EXPECT_TRUE(action->IsEqual(*vect[4]));
330 
331   int64_t size = 0;
332   ASSERT_TRUE(base::GetFileSize(filepath(), &size));
333   ASSERT_EQ(0, size);
334 }
335 
TEST_F(SerializationUtilsTest,WriteReadTest_DeleteFile)336 TEST_F(SerializationUtilsTest, WriteReadTest_DeleteFile) {
337   std::unique_ptr<MetricSample> hist = MetricSample::HistogramSample(
338       "myhist", /*sample=*/1, /*min=*/2, /*max=*/3, /*bucket_count=*/4,
339       /*num_samples=*/5);
340   std::unique_ptr<MetricSample> crash =
341       MetricSample::CrashSample("mycrash", /*num_samples=*/10);
342   std::unique_ptr<MetricSample> lhist = MetricSample::LinearHistogramSample(
343       "linear", /*sample=*/1, /*max=*/10, /*num_samples=*/10);
344   std::unique_ptr<MetricSample> shist = MetricSample::SparseHistogramSample(
345       "mysparse", /*sample=*/30, /*num_samples=*/10);
346   std::unique_ptr<MetricSample> action =
347       MetricSample::UserActionSample("myaction", /*num_samples=*/1);
348 
349   SerializationUtils::WriteMetricToFile(*hist.get(), filename());
350   SerializationUtils::WriteMetricToFile(*crash.get(), filename());
351   SerializationUtils::WriteMetricToFile(*lhist.get(), filename());
352   SerializationUtils::WriteMetricToFile(*shist.get(), filename());
353   SerializationUtils::WriteMetricToFile(*action.get(), filename());
354   std::vector<std::unique_ptr<MetricSample>> vect;
355   SerializationUtils::ReadAndDeleteMetricsFromFile(filename(), &vect);
356   // NOTE: Should *not* have an entry for each repeated sample.
357   ASSERT_EQ(vect.size(), size_t(5));
358   for (auto& sample : vect) {
359     ASSERT_NE(nullptr, sample.get());
360   }
361   EXPECT_TRUE(hist->IsEqual(*vect[0]));
362   EXPECT_TRUE(crash->IsEqual(*vect[1]));
363   EXPECT_TRUE(lhist->IsEqual(*vect[2]));
364   EXPECT_TRUE(shist->IsEqual(*vect[3]));
365   EXPECT_TRUE(action->IsEqual(*vect[4]));
366 
367   EXPECT_FALSE(base::PathExists(filepath()));
368 }
369 
TEST_F(SerializationUtilsTest,TooManyMessagesTest)370 TEST_F(SerializationUtilsTest, TooManyMessagesTest) {
371   std::unique_ptr<MetricSample> hist = MetricSample::HistogramSample(
372       "myhist", /*sample=*/1, /*min=*/2, /*max=*/3, /*bucket_count=*/4,
373       /*num_samples=*/5);
374 
375   constexpr int kDiscardedSamples = 50000;
376   for (int i = 0;
377        i < SerializationUtils::kMaxMessagesPerRead + kDiscardedSamples; i++) {
378     SerializationUtils::WriteMetricToFile(*hist.get(), filename());
379   }
380 
381   std::vector<std::unique_ptr<MetricSample>> vect;
382   SerializationUtils::ReadAndTruncateMetricsFromFile(filename(), &vect);
383   ASSERT_EQ(SerializationUtils::kMaxMessagesPerRead,
384             static_cast<int>(vect.size()));
385   for (auto& sample : vect) {
386     ASSERT_NE(nullptr, sample.get());
387     EXPECT_TRUE(hist->IsEqual(*sample));
388   }
389 
390   int64_t size = 0;
391   ASSERT_TRUE(base::GetFileSize(filepath(), &size));
392   ASSERT_EQ(0, size);
393 }
394 
TEST_F(SerializationUtilsTest,ReadEmptyFile)395 TEST_F(SerializationUtilsTest, ReadEmptyFile) {
396   {
397     // Create a zero-length file and then close file descriptor.
398     base::File file(filepath(),
399                     base::File::FLAG_CREATE | base::File::FLAG_WRITE);
400     ASSERT_TRUE(file.IsValid());
401   }
402 
403   std::vector<std::unique_ptr<MetricSample>> vect;
404   SerializationUtils::ReadAndTruncateMetricsFromFile(filename(), &vect);
405   EXPECT_THAT(vect, IsEmpty());
406 }
407 
TEST_F(SerializationUtilsTest,ReadNonExistentFile)408 TEST_F(SerializationUtilsTest, ReadNonExistentFile) {
409   base::DeleteFile(filepath());  // Ensure non-existance.
410   base::HistogramTester histogram_tester;
411   std::vector<std::unique_ptr<MetricSample>> vect;
412   SerializationUtils::ReadAndTruncateMetricsFromFile(filename(), &vect);
413   EXPECT_THAT(vect, IsEmpty());
414 }
415 
TEST_F(SerializationUtilsTest,ParseInvalidType)416 TEST_F(SerializationUtilsTest, ParseInvalidType) {
417   // Verify that parsing of an invalid sample type fails.
418   EXPECT_EQ(nullptr, SerializationUtils::ParseSample(base::StringPrintf(
419                          "not_a_type%cvalue%c", '\0', '\0')));
420 }
421 
422 }  // namespace
423 }  // namespace metrics
424