xref: /aosp_15_r20/external/cronet/components/metrics/unsent_log_store_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/unsent_log_store.h"
6 
7 #include <stddef.h>
8 #include <limits>
9 
10 #include "base/base64.h"
11 #include "base/hash/sha1.h"
12 #include "base/rand_util.h"
13 #include "base/values.h"
14 #include "components/metrics/unsent_log_store_metrics_impl.h"
15 #include "components/prefs/pref_registry_simple.h"
16 #include "components/prefs/scoped_user_pref_update.h"
17 #include "components/prefs/testing_pref_service.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19 #include "third_party/zlib/google/compression_utils.h"
20 
21 namespace metrics {
22 
23 namespace {
24 
25 const char kTestPrefName[] = "TestPref";
26 const char kTestMetaDataPrefName[] = "TestMetaDataPref";
27 
28 const size_t kLogCountLimit = 3;
29 const size_t kLogByteLimit = 1000;
30 
31 // Compresses |log_data| and returns the result.
Compress(const std::string & log_data)32 std::string Compress(const std::string& log_data) {
33   std::string compressed_log_data;
34   EXPECT_TRUE(compression::GzipCompress(log_data, &compressed_log_data));
35   return compressed_log_data;
36 }
37 
38 // Generates and returns log data such that its size after compression is at
39 // least |min_compressed_size|.
GenerateLogWithMinCompressedSize(size_t min_compressed_size)40 std::string GenerateLogWithMinCompressedSize(size_t min_compressed_size) {
41   // Since the size check is done against a compressed log, generate enough
42   // data that compresses to larger than |log_size|.
43   std::string rand_bytes = base::RandBytesAsString(min_compressed_size);
44   while (Compress(rand_bytes).size() < min_compressed_size)
45     rand_bytes.append(base::RandBytesAsString(min_compressed_size));
46   SCOPED_TRACE(testing::Message()
47                << "Using random data " << base::Base64Encode(rand_bytes));
48   return rand_bytes;
49 }
50 
51 class UnsentLogStoreTest : public testing::Test {
52  public:
UnsentLogStoreTest()53   UnsentLogStoreTest() {
54     prefs_.registry()->RegisterListPref(kTestPrefName);
55     prefs_.registry()->RegisterDictionaryPref(kTestMetaDataPrefName);
56   }
57 
58   UnsentLogStoreTest(const UnsentLogStoreTest&) = delete;
59   UnsentLogStoreTest& operator=(const UnsentLogStoreTest&) = delete;
60 
61  protected:
62   TestingPrefServiceSimple prefs_;
63 };
64 
65 class TestUnsentLogStoreMetrics : public UnsentLogStoreMetrics {
66  public:
67   TestUnsentLogStoreMetrics() = default;
68 
RecordLastUnsentLogMetadataMetrics(int unsent_samples_count,int sent_samples_count,int persisted_size_in_kb)69   void RecordLastUnsentLogMetadataMetrics(int unsent_samples_count,
70                                           int sent_samples_count,
71                                           int persisted_size_in_kb) override {
72     unsent_samples_count_ = unsent_samples_count;
73     sent_samples_count_ = sent_samples_count;
74     persisted_size_in_kb_ = persisted_size_in_kb;
75   }
76 
unsent_samples_count() const77   int unsent_samples_count() const { return unsent_samples_count_; }
sent_samples_count() const78   int sent_samples_count() const { return sent_samples_count_; }
persisted_size_in_kb() const79   int persisted_size_in_kb() const { return persisted_size_in_kb_; }
80 
81  private:
82   int unsent_samples_count_ = 0;
83   int sent_samples_count_ = 0;
84   int persisted_size_in_kb_ = 0;
85 };
86 
87 class TestUnsentLogStore : public UnsentLogStore {
88  public:
TestUnsentLogStore(PrefService * service,size_t min_log_bytes)89   TestUnsentLogStore(PrefService* service, size_t min_log_bytes)
90       : UnsentLogStore(std::make_unique<UnsentLogStoreMetricsImpl>(),
91                        service,
92                        kTestPrefName,
93                        /*metadata_pref_name=*/nullptr,
94                        UnsentLogStore::UnsentLogStoreLimits{
95                            .min_log_count = kLogCountLimit,
96                            .min_queue_size_bytes = min_log_bytes,
97                        },
98                        /*signing_key=*/std::string(),
99                        /*logs_event_manager=*/nullptr) {}
TestUnsentLogStore(PrefService * service,size_t min_log_bytes,const std::string & signing_key)100   TestUnsentLogStore(PrefService* service,
101                      size_t min_log_bytes,
102                      const std::string& signing_key)
103       : UnsentLogStore(std::make_unique<UnsentLogStoreMetricsImpl>(),
104                        service,
105                        kTestPrefName,
106                        /*metadata_pref_name=*/nullptr,
107                        UnsentLogStore::UnsentLogStoreLimits{
108                            .min_log_count = kLogCountLimit,
109                            .min_queue_size_bytes = min_log_bytes,
110                        },
111                        signing_key,
112                        /*logs_event_manager=*/nullptr) {}
TestUnsentLogStore(std::unique_ptr<UnsentLogStoreMetrics> metrics,PrefService * service,size_t max_log_size)113   TestUnsentLogStore(std::unique_ptr<UnsentLogStoreMetrics> metrics,
114                      PrefService* service,
115                      size_t max_log_size)
116       : UnsentLogStore(std::move(metrics),
117                        service,
118                        kTestPrefName,
119                        kTestMetaDataPrefName,
120                        UnsentLogStore::UnsentLogStoreLimits{
121                            .min_log_count = kLogCountLimit,
122                            .min_queue_size_bytes = 1,
123                            .max_log_size_bytes = max_log_size,
124                        },
125                        /*signing_key=*/std::string(),
126                        /*logs_event_manager=*/nullptr) {}
127 
128   TestUnsentLogStore(const TestUnsentLogStore&) = delete;
129   TestUnsentLogStore& operator=(const TestUnsentLogStore&) = delete;
130 
131   // Stages and removes the next log, while testing it's value.
ExpectNextLog(const std::string & expected_log)132   void ExpectNextLog(const std::string& expected_log) {
133     StageNextLog();
134     EXPECT_EQ(staged_log(), Compress(expected_log));
135     DiscardStagedLog();
136   }
137 };
138 
139 }  // namespace
140 
141 // Store and retrieve empty list_value.
TEST_F(UnsentLogStoreTest,EmptyLogList)142 TEST_F(UnsentLogStoreTest, EmptyLogList) {
143   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
144 
145   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
146   EXPECT_EQ(0U, prefs_.GetList(kTestPrefName).size());
147 
148   TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
149   result_unsent_log_store.LoadPersistedUnsentLogs();
150   EXPECT_EQ(0U, result_unsent_log_store.size());
151 }
152 
153 // Store and retrieve a single log value.
TEST_F(UnsentLogStoreTest,SingleElementLogList)154 TEST_F(UnsentLogStoreTest, SingleElementLogList) {
155   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
156 
157   LogMetadata log_metadata;
158   unsent_log_store.StoreLog("Hello world!", log_metadata,
159                             MetricsLogsEventManager::CreateReason::kUnknown);
160   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
161 
162   TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
163   result_unsent_log_store.LoadPersistedUnsentLogs();
164   EXPECT_EQ(1U, result_unsent_log_store.size());
165 
166   // Verify that the result log matches the initial log.
167   unsent_log_store.StageNextLog();
168   result_unsent_log_store.StageNextLog();
169   EXPECT_EQ(unsent_log_store.staged_log(),
170             result_unsent_log_store.staged_log());
171   EXPECT_EQ(unsent_log_store.staged_log_hash(),
172             result_unsent_log_store.staged_log_hash());
173   EXPECT_EQ(unsent_log_store.staged_log_signature(),
174             result_unsent_log_store.staged_log_signature());
175   EXPECT_EQ(unsent_log_store.staged_log_timestamp(),
176             result_unsent_log_store.staged_log_timestamp());
177 }
178 
179 // Store a set of logs over the length limit, but smaller than the min number of
180 // bytes. This should leave the logs unchanged.
TEST_F(UnsentLogStoreTest,LongButTinyLogList)181 TEST_F(UnsentLogStoreTest, LongButTinyLogList) {
182   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
183   LogMetadata log_metadata;
184 
185   size_t log_count = kLogCountLimit * 5;
186   for (size_t i = 0; i < log_count; ++i)
187     unsent_log_store.StoreLog("x", log_metadata,
188                               MetricsLogsEventManager::CreateReason::kUnknown);
189 
190   EXPECT_EQ(log_count, unsent_log_store.size());
191   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
192   EXPECT_EQ(log_count, unsent_log_store.size());
193 
194   TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
195   result_unsent_log_store.LoadPersistedUnsentLogs();
196   EXPECT_EQ(unsent_log_store.size(), result_unsent_log_store.size());
197 
198   result_unsent_log_store.ExpectNextLog("x");
199 }
200 
201 // Store a set of logs over the length limit, but that doesn't reach the minimum
202 // number of bytes until after passing the length limit.
TEST_F(UnsentLogStoreTest,LongButSmallLogList)203 TEST_F(UnsentLogStoreTest, LongButSmallLogList) {
204   size_t log_count = kLogCountLimit * 5;
205   size_t log_size = 50;
206   LogMetadata log_metadata;
207 
208   std::string first_kept = "First to keep";
209   first_kept.resize(log_size, ' ');
210 
211   std::string blank_log = std::string(log_size, ' ');
212 
213   std::string last_kept = "Last to keep";
214   last_kept.resize(log_size, ' ');
215 
216   // Set the byte limit enough to keep everything but the first two logs.
217   const size_t min_log_bytes = Compress(first_kept).length() +
218                                Compress(last_kept).length() +
219                                (log_count - 4) * Compress(blank_log).length();
220   TestUnsentLogStore unsent_log_store(&prefs_, min_log_bytes);
221 
222   unsent_log_store.StoreLog("one", log_metadata,
223                             MetricsLogsEventManager::CreateReason::kUnknown);
224   unsent_log_store.StoreLog("two", log_metadata,
225                             MetricsLogsEventManager::CreateReason::kUnknown);
226   unsent_log_store.StoreLog(first_kept, log_metadata,
227                             MetricsLogsEventManager::CreateReason::kUnknown);
228   for (size_t i = unsent_log_store.size(); i < log_count - 1; ++i) {
229     unsent_log_store.StoreLog(blank_log, log_metadata,
230                               MetricsLogsEventManager::CreateReason::kUnknown);
231   }
232   unsent_log_store.StoreLog(last_kept, log_metadata,
233                             MetricsLogsEventManager::CreateReason::kUnknown);
234 
235   size_t original_size = unsent_log_store.size();
236   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
237   // New size has been reduced.
238   EXPECT_EQ(original_size - 2, unsent_log_store.size());
239 
240   TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
241   result_unsent_log_store.LoadPersistedUnsentLogs();
242   // Prefs should be the same size.
243   EXPECT_EQ(unsent_log_store.size(), result_unsent_log_store.size());
244 
245   result_unsent_log_store.ExpectNextLog(last_kept);
246   while (result_unsent_log_store.size() > 1) {
247     result_unsent_log_store.ExpectNextLog(blank_log);
248   }
249   result_unsent_log_store.ExpectNextLog(first_kept);
250 }
251 
252 // Store a set of logs within the length limit, but well over the minimum
253 // number of bytes. This should leave the logs unchanged.
TEST_F(UnsentLogStoreTest,ShortButLargeLogList)254 TEST_F(UnsentLogStoreTest, ShortButLargeLogList) {
255   // Make the total byte count about twice the minimum.
256   size_t log_count = kLogCountLimit;
257   size_t log_size = (kLogByteLimit / log_count) * 2;
258   std::string log_data = GenerateLogWithMinCompressedSize(log_size);
259 
260   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
261   LogMetadata log_metadata;
262   for (size_t i = 0; i < log_count; ++i) {
263     unsent_log_store.StoreLog(log_data, log_metadata,
264                               MetricsLogsEventManager::CreateReason::kUnknown);
265   }
266   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
267 
268   TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
269   result_unsent_log_store.LoadPersistedUnsentLogs();
270   // Both have expected number of logs (original amount).
271   EXPECT_EQ(kLogCountLimit, unsent_log_store.size());
272   EXPECT_EQ(kLogCountLimit, result_unsent_log_store.size());
273 }
274 
275 // Store a set of logs over the length limit, and over the minimum number of
276 // bytes. This will trim the set of logs.
TEST_F(UnsentLogStoreTest,LongAndLargeLogList)277 TEST_F(UnsentLogStoreTest, LongAndLargeLogList) {
278   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
279 
280   // Include twice the max number of logs.
281   size_t log_count = kLogCountLimit * 2;
282   // Make the total byte count about four times the minimum.
283   size_t log_size = (kLogByteLimit / log_count) * 4;
284 
285   std::string target_log = "First to keep";
286   target_log += GenerateLogWithMinCompressedSize(log_size);
287 
288   std::string log_data = GenerateLogWithMinCompressedSize(log_size);
289   LogMetadata log_metadata;
290   for (size_t i = 0; i < log_count; ++i) {
291     if (i == log_count - kLogCountLimit)
292       unsent_log_store.StoreLog(
293           target_log, log_metadata,
294           MetricsLogsEventManager::CreateReason::kUnknown);
295     else
296       unsent_log_store.StoreLog(
297           log_data, log_metadata,
298           MetricsLogsEventManager::CreateReason::kUnknown);
299   }
300 
301   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
302 
303   TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
304   result_unsent_log_store.LoadPersistedUnsentLogs();
305   // Both original log and persisted are reduced to limit.
306   EXPECT_EQ(kLogCountLimit, unsent_log_store.size());
307   EXPECT_EQ(kLogCountLimit, result_unsent_log_store.size());
308 
309   while (result_unsent_log_store.size() > 1) {
310     result_unsent_log_store.ExpectNextLog(log_data);
311   }
312   result_unsent_log_store.ExpectNextLog(target_log);
313 }
314 
315 // Store a set of logs over the length limit, and over the minimum number of
316 // bytes. The first log will be a staged log that should be trimmed away. This
317 // should make the log store not have a staged log anymore.
TEST_F(UnsentLogStoreTest,TrimStagedLog)318 TEST_F(UnsentLogStoreTest, TrimStagedLog) {
319   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
320 
321   // Make each log byte count the limit.
322   size_t log_size = kLogByteLimit;
323 
324   // Create a target log that will be the staged log that we want to trim away.
325   std::string target_log = "First that should be trimmed";
326   target_log += GenerateLogWithMinCompressedSize(log_size);
327   LogMetadata log_metadata;
328   unsent_log_store.StoreLog(target_log, log_metadata,
329                             MetricsLogsEventManager::CreateReason::kUnknown);
330   unsent_log_store.StageNextLog();
331   EXPECT_TRUE(unsent_log_store.has_staged_log());
332 
333   // Add |kLogCountLimit| additional logs.
334   std::string log_data = GenerateLogWithMinCompressedSize(log_size);
335   for (size_t i = 0; i < kLogCountLimit; ++i) {
336     unsent_log_store.StoreLog(log_data, log_metadata,
337                               MetricsLogsEventManager::CreateReason::kUnknown);
338   }
339 
340   EXPECT_EQ(kLogCountLimit + 1, unsent_log_store.size());
341   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
342 
343   // Verify that the first log (the staged one) was trimmed away, and that the
344   // log store does not consider to have any staged log anymore. The other logs
345   // are not trimmed because the most recent logs are prioritized and we trim
346   // until we have |kLogCountLimit| logs.
347   EXPECT_EQ(kLogCountLimit, unsent_log_store.size());
348   EXPECT_FALSE(unsent_log_store.has_staged_log());
349   // Verify that all of the logs in the log store are not the |target_log|.
350   while (unsent_log_store.size() > 0) {
351     unsent_log_store.ExpectNextLog(log_data);
352   }
353 }
354 
355 // Verifies that when calling TrimAndPersistUnsentLogs() with
356 // |overwrite_in_memory_store| set to false, the in memory log store is
357 // unaffected.
TEST_F(UnsentLogStoreTest,TrimAndPersistUnsentLogs_DoNotOverwriteInMemoryStore)358 TEST_F(UnsentLogStoreTest,
359        TrimAndPersistUnsentLogs_DoNotOverwriteInMemoryStore) {
360   TestUnsentLogStore unsent_log_store(
361       std::make_unique<UnsentLogStoreMetricsImpl>(), &prefs_, kLogByteLimit);
362 
363   LogMetadata log_metadata;
364   std::string log_data = GenerateLogWithMinCompressedSize(kLogByteLimit + 1);
365   unsent_log_store.StoreLog(log_data, log_metadata,
366                             MetricsLogsEventManager::CreateReason::kUnknown);
367   unsent_log_store.TrimAndPersistUnsentLogs(
368       /*overwrite_in_memory_store=*/false);
369 
370   // Verify that the log store still contains the log.
371   EXPECT_EQ(1U, unsent_log_store.size());
372   unsent_log_store.ExpectNextLog(log_data);
373 
374   // Verify that the log was trimmed when persisted to memory.
375   TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
376   result_unsent_log_store.LoadPersistedUnsentLogs();
377   EXPECT_EQ(0U, result_unsent_log_store.size());
378 }
379 
380 // Verifies that TrimAndPersistUnsentLogs() maintains the log order.
TEST_F(UnsentLogStoreTest,TrimAndPersistUnsentLogs_MaintainsLogOrder)381 TEST_F(UnsentLogStoreTest, TrimAndPersistUnsentLogs_MaintainsLogOrder) {
382   TestUnsentLogStore unsent_log_store(
383       std::make_unique<UnsentLogStoreMetricsImpl>(), &prefs_, kLogByteLimit);
384 
385   LogMetadata log_metadata;
386   unsent_log_store.StoreLog("1", log_metadata,
387                             MetricsLogsEventManager::CreateReason::kUnknown);
388   unsent_log_store.StoreLog(GenerateLogWithMinCompressedSize(kLogByteLimit + 1),
389                             log_metadata,
390                             MetricsLogsEventManager::CreateReason::kUnknown);
391   unsent_log_store.StoreLog("2", log_metadata,
392                             MetricsLogsEventManager::CreateReason::kUnknown);
393   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
394 
395   // Verify that only the second log was trimmed (since it was over the max log
396   // size), and that the log order was maintained. I.e., "2" should be the most
397   // recent log, followed by "1".
398   EXPECT_EQ(2U, unsent_log_store.size());
399   unsent_log_store.ExpectNextLog("2");
400   unsent_log_store.ExpectNextLog("1");
401 
402   // Similarly, verify that the order was also maintained in persistent memory.
403   TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
404   result_unsent_log_store.LoadPersistedUnsentLogs();
405   EXPECT_EQ(2U, result_unsent_log_store.size());
406   result_unsent_log_store.ExpectNextLog("2");
407   result_unsent_log_store.ExpectNextLog("1");
408 }
409 
410 // Verifies that calling TrimAndPersistUnsentLogs() clears the pref list before
411 // writing the trimmed logs list.
TEST_F(UnsentLogStoreTest,TrimAndPersistUnsentLogs_OverwritesPrefs)412 TEST_F(UnsentLogStoreTest, TrimAndPersistUnsentLogs_OverwritesPrefs) {
413   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
414 
415   LogMetadata log_metadata;
416   unsent_log_store.StoreLog("Hello world!", log_metadata,
417                             MetricsLogsEventManager::CreateReason::kUnknown);
418   // Call TrimAndPersistUnsentLogs(). The log should not be trimmed.
419   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
420 
421   TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
422   result_unsent_log_store.LoadPersistedUnsentLogs();
423   EXPECT_EQ(1U, result_unsent_log_store.size());
424 
425   // Verify that the result log matches the initial log.
426   result_unsent_log_store.ExpectNextLog("Hello world!");
427 
428   // Call TrimAndPersistUnsentLogs() and load the persisted logs once again.
429   // There should still only be one log.
430   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
431 
432   TestUnsentLogStore result_unsent_log_store2(&prefs_, kLogByteLimit);
433   result_unsent_log_store2.LoadPersistedUnsentLogs();
434   EXPECT_EQ(1U, result_unsent_log_store2.size());
435 
436   // Verify that the result log matches the initial log.
437   result_unsent_log_store2.ExpectNextLog("Hello world!");
438 }
439 
440 // Check that the store/stage/discard functions work as expected.
TEST_F(UnsentLogStoreTest,Staging)441 TEST_F(UnsentLogStoreTest, Staging) {
442   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
443   LogMetadata log_metadata;
444   std::string tmp;
445 
446   EXPECT_FALSE(unsent_log_store.has_staged_log());
447   unsent_log_store.StoreLog("one", log_metadata,
448                             MetricsLogsEventManager::CreateReason::kUnknown);
449   EXPECT_FALSE(unsent_log_store.has_staged_log());
450   unsent_log_store.StoreLog("two", log_metadata,
451                             MetricsLogsEventManager::CreateReason::kUnknown);
452   unsent_log_store.StageNextLog();
453   EXPECT_TRUE(unsent_log_store.has_staged_log());
454   EXPECT_EQ(unsent_log_store.staged_log(), Compress("two"));
455   unsent_log_store.StoreLog("three", log_metadata,
456                             MetricsLogsEventManager::CreateReason::kUnknown);
457   EXPECT_EQ(unsent_log_store.staged_log(), Compress("two"));
458   EXPECT_EQ(unsent_log_store.size(), 3U);
459   unsent_log_store.DiscardStagedLog();
460   EXPECT_FALSE(unsent_log_store.has_staged_log());
461   EXPECT_EQ(unsent_log_store.size(), 2U);
462   unsent_log_store.StageNextLog();
463   EXPECT_EQ(unsent_log_store.staged_log(), Compress("three"));
464   unsent_log_store.DiscardStagedLog();
465   unsent_log_store.StageNextLog();
466   EXPECT_EQ(unsent_log_store.staged_log(), Compress("one"));
467   unsent_log_store.DiscardStagedLog();
468   EXPECT_FALSE(unsent_log_store.has_staged_log());
469   EXPECT_EQ(unsent_log_store.size(), 0U);
470 }
471 
TEST_F(UnsentLogStoreTest,DiscardOrder)472 TEST_F(UnsentLogStoreTest, DiscardOrder) {
473   // Ensure that the correct log is discarded if new logs are pushed while
474   // a log is staged.
475   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
476   LogMetadata log_metadata;
477 
478   unsent_log_store.StoreLog("one", log_metadata,
479                             MetricsLogsEventManager::CreateReason::kUnknown);
480   unsent_log_store.StageNextLog();
481   unsent_log_store.StoreLog("two", log_metadata,
482                             MetricsLogsEventManager::CreateReason::kUnknown);
483   unsent_log_store.DiscardStagedLog();
484   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
485 
486   TestUnsentLogStore result_unsent_log_store(&prefs_, kLogByteLimit);
487   result_unsent_log_store.LoadPersistedUnsentLogs();
488   EXPECT_EQ(1U, result_unsent_log_store.size());
489   result_unsent_log_store.ExpectNextLog("two");
490 }
491 
TEST_F(UnsentLogStoreTest,Hashes)492 TEST_F(UnsentLogStoreTest, Hashes) {
493   const char kFooText[] = "foo";
494   const std::string foo_hash = base::SHA1HashString(kFooText);
495   LogMetadata log_metadata;
496 
497   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
498   unsent_log_store.StoreLog(kFooText, log_metadata,
499                             MetricsLogsEventManager::CreateReason::kUnknown);
500   unsent_log_store.StageNextLog();
501 
502   EXPECT_EQ(Compress(kFooText), unsent_log_store.staged_log());
503   EXPECT_EQ(foo_hash, unsent_log_store.staged_log_hash());
504 }
505 
TEST_F(UnsentLogStoreTest,Signatures)506 TEST_F(UnsentLogStoreTest, Signatures) {
507   const char kFooText[] = "foo";
508   LogMetadata log_metadata;
509 
510   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
511   unsent_log_store.StoreLog(kFooText, log_metadata,
512                             MetricsLogsEventManager::CreateReason::kUnknown);
513   unsent_log_store.StageNextLog();
514 
515   EXPECT_EQ(Compress(kFooText), unsent_log_store.staged_log());
516 
517   // The expected signature as a base 64 encoded string. The value was obtained
518   // by running the test with an empty expected_signature_base64 and taking the
519   // actual value from the test failure message. Can be verifying by the
520   // following python code:
521   // import hmac, hashlib, base64
522   // key = ''
523   // print(base64.b64encode(
524   //   hmac.new(key, msg='foo', digestmod=hashlib.sha256).digest()).decode())
525   std::string expected_signature_base64 =
526       "DA2Y9+PZ1F5y6Id7wbEEMn77nAexjy/+ztdtgTB/H/8=";
527 
528   std::string actual_signature_base64 =
529       base::Base64Encode(unsent_log_store.staged_log_signature());
530   EXPECT_EQ(expected_signature_base64, actual_signature_base64);
531 
532   // Test a different key results in a different signature.
533   std::string key = "secret key, don't tell anyone";
534   TestUnsentLogStore unsent_log_store_different_key(&prefs_, kLogByteLimit,
535                                                     key);
536 
537   unsent_log_store_different_key.StoreLog(
538       kFooText, log_metadata, MetricsLogsEventManager::CreateReason::kUnknown);
539   unsent_log_store_different_key.StageNextLog();
540 
541   EXPECT_EQ(Compress(kFooText), unsent_log_store_different_key.staged_log());
542 
543   // Base 64 encoded signature obtained in similar fashion to previous
544   // signature. To use previous python code change:
545   // key = "secret key, don't tell anyone"
546   expected_signature_base64 = "DV7z8wdDrjLkQrCzrXR3UjWsR3/YVM97tIhMnhUvfXM=";
547   actual_signature_base64 =
548       base::Base64Encode(unsent_log_store_different_key.staged_log_signature());
549 
550   EXPECT_EQ(expected_signature_base64, actual_signature_base64);
551 }
552 
TEST_F(UnsentLogStoreTest,StoreLogWithUserId)553 TEST_F(UnsentLogStoreTest, StoreLogWithUserId) {
554   const char foo_text[] = "foo";
555   const uint64_t user_id = 12345L;
556 
557   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
558   LogMetadata log_metadata(std::nullopt, user_id, std::nullopt);
559   unsent_log_store.StoreLog(foo_text, log_metadata,
560                             MetricsLogsEventManager::CreateReason::kUnknown);
561   unsent_log_store.StageNextLog();
562 
563   EXPECT_EQ(Compress(foo_text), unsent_log_store.staged_log());
564   EXPECT_EQ(unsent_log_store.staged_log_user_id().value(), user_id);
565 
566   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
567 
568   // Reads persisted logs from new log store.
569   TestUnsentLogStore read_unsent_log_store(&prefs_, kLogByteLimit);
570   read_unsent_log_store.LoadPersistedUnsentLogs();
571   EXPECT_EQ(1U, read_unsent_log_store.size());
572 
573   // Ensure that the user_id was parsed correctly.
574   read_unsent_log_store.StageNextLog();
575   EXPECT_EQ(user_id, read_unsent_log_store.staged_log_user_id().value());
576 }
577 
TEST_F(UnsentLogStoreTest,StoreLogWithLargeUserId)578 TEST_F(UnsentLogStoreTest, StoreLogWithLargeUserId) {
579   const char foo_text[] = "foo";
580   const uint64_t large_user_id = std::numeric_limits<uint64_t>::max();
581 
582   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
583   LogMetadata log_metadata(std::nullopt, large_user_id, std::nullopt);
584   unsent_log_store.StoreLog(foo_text, log_metadata,
585                             MetricsLogsEventManager::CreateReason::kUnknown);
586   unsent_log_store.StageNextLog();
587 
588   EXPECT_EQ(Compress(foo_text), unsent_log_store.staged_log());
589   EXPECT_EQ(unsent_log_store.staged_log_user_id().value(), large_user_id);
590 
591   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
592 
593   // Reads persisted logs from new log store.
594   TestUnsentLogStore read_unsent_log_store(&prefs_, kLogByteLimit);
595   read_unsent_log_store.LoadPersistedUnsentLogs();
596   EXPECT_EQ(1U, read_unsent_log_store.size());
597 
598   // Ensure that the user_id was parsed correctly.
599   read_unsent_log_store.StageNextLog();
600   EXPECT_EQ(large_user_id, read_unsent_log_store.staged_log_user_id().value());
601 }
602 
TEST_F(UnsentLogStoreTest,StoreLogWithOnlyAppKMLogSource)603 TEST_F(UnsentLogStoreTest, StoreLogWithOnlyAppKMLogSource) {
604   const char foo_text[] = "foo";
605   const UkmLogSourceType log_source_type = UkmLogSourceType::APPKM_ONLY;
606 
607   TestUnsentLogStore unsent_log_store(&prefs_, kLogByteLimit);
608   LogMetadata log_metadata(std::nullopt, std::nullopt, log_source_type);
609   unsent_log_store.StoreLog(foo_text, log_metadata,
610                             MetricsLogsEventManager::CreateReason::kUnknown);
611   unsent_log_store.StageNextLog();
612 
613   EXPECT_EQ(Compress(foo_text), unsent_log_store.staged_log());
614   EXPECT_EQ(unsent_log_store.staged_log_metadata().log_source_type.value(),
615             log_source_type);
616 
617   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
618 
619   // Reads persisted logs from new log store.
620   TestUnsentLogStore read_unsent_log_store(&prefs_, kLogByteLimit);
621   read_unsent_log_store.LoadPersistedUnsentLogs();
622   EXPECT_EQ(1U, read_unsent_log_store.size());
623 
624   // Ensure that the log source type was updated correctly in log metadata.
625   read_unsent_log_store.StageNextLog();
626   EXPECT_EQ(
627       log_source_type,
628       read_unsent_log_store.staged_log_metadata().log_source_type.value());
629 }
630 
TEST_F(UnsentLogStoreTest,UnsentLogMetadataMetrics)631 TEST_F(UnsentLogStoreTest, UnsentLogMetadataMetrics) {
632   std::unique_ptr<TestUnsentLogStoreMetrics> metrics =
633       std::make_unique<TestUnsentLogStoreMetrics>();
634   TestUnsentLogStoreMetrics* m = metrics.get();
635   TestUnsentLogStore unsent_log_store(std::move(metrics), &prefs_,
636                                       kLogByteLimit * 10);
637 
638   // Prepare 4 logs.
639   const char kFooText[] = "foo";
640   const base::HistogramBase::Count kFooSampleCount = 3;
641 
642   // The |foobar_log| whose compressed size is over 1kb will be staged first, so
643   // the persisted_size_in_kb shall be reduced by 1kb afterwards.
644   std::string foobar_log = GenerateLogWithMinCompressedSize(1024);
645   const base::HistogramBase::Count kFooBarSampleCount = 5;
646 
647   // The |oversize_log| shall not be persisted.
648   std::string oversize_log =
649       GenerateLogWithMinCompressedSize(kLogByteLimit * 10 + 1);
650   const base::HistogramBase::Count kOversizeLogSampleCount = 50;
651 
652   // The log without the SampleCount will not be counted to metrics.
653   const char kNoSampleLog[] = "no sample log";
654 
655   LogMetadata log_metadata_with_oversize_sample(kOversizeLogSampleCount,
656                                                 std::nullopt, std::nullopt);
657   unsent_log_store.StoreLog(oversize_log, log_metadata_with_oversize_sample,
658                             MetricsLogsEventManager::CreateReason::kUnknown);
659 
660   LogMetadata log_metadata_with_no_sample;
661   unsent_log_store.StoreLog(kNoSampleLog, log_metadata_with_no_sample,
662                             MetricsLogsEventManager::CreateReason::kUnknown);
663 
664   LogMetadata log_metadata_foo_sample(kFooSampleCount, std::nullopt,
665                                       std::nullopt);
666   unsent_log_store.StoreLog(kFooText, log_metadata_foo_sample,
667                             MetricsLogsEventManager::CreateReason::kUnknown);
668 
669   // The foobar_log will be staged first.
670   LogMetadata log_metadata_foo_bar_sample(kFooBarSampleCount, std::nullopt,
671                                           std::nullopt);
672   unsent_log_store.StoreLog(foobar_log, log_metadata_foo_bar_sample,
673                             MetricsLogsEventManager::CreateReason::kUnknown);
674 
675   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
676 
677   unsent_log_store.RecordMetaDataMetrics();
678   // The |oversize_log| was ignored, the kNoSampleLog won't be counted to
679   // metrics,
680   EXPECT_EQ(kFooSampleCount + kFooBarSampleCount, m->unsent_samples_count());
681   EXPECT_EQ(0, m->sent_samples_count());
682   EXPECT_EQ(2, m->persisted_size_in_kb());
683 
684   // Pretend to send log.
685   unsent_log_store.StageNextLog();
686   unsent_log_store.MarkStagedLogAsSent();
687   unsent_log_store.DiscardStagedLog();
688   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
689   unsent_log_store.RecordMetaDataMetrics();
690 
691   // The |foobar_log| shall be sent.
692   EXPECT_EQ(kFooSampleCount, m->unsent_samples_count());
693   EXPECT_EQ(kFooBarSampleCount, m->sent_samples_count());
694   EXPECT_EQ(1, m->persisted_size_in_kb());
695 
696   // Pretend |kFooText| upload failure.
697   unsent_log_store.StageNextLog();
698   unsent_log_store.DiscardStagedLog();
699   unsent_log_store.TrimAndPersistUnsentLogs(/*overwrite_in_memory_store=*/true);
700   unsent_log_store.RecordMetaDataMetrics();
701 
702   // Verify the failed upload wasn't added to the sent samples count.
703   EXPECT_EQ(0, m->unsent_samples_count());
704   EXPECT_EQ(kFooBarSampleCount, m->sent_samples_count());
705   EXPECT_EQ(1, m->persisted_size_in_kb());
706 }
707 
708 }  // namespace metrics
709